Merge pull request #3987 from mhugo/servermultithread

[server] parallel map rendering
This commit is contained in:
Hugo Mercier 2017-01-13 16:16:14 +01:00 committed by GitHub
commit ef8a135f1b
44 changed files with 1168 additions and 104 deletions

View File

@ -0,0 +1,89 @@
/***************************************************************************
qgsserversettings.sip
---------------------
begin : December 19, 2016
copyright : (C) 2016 by Paul Blottiere
email : paul dot blottiere at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/** \ingroup server
* QgsServerSettings provides a way to retrieve settings by prioritizing
* according to environment variables, ini file and default values.
* @note added in QGIS 3.0
*/
class QgsServerSettings
{
%TypeHeaderCode
#include "qgsserversettings.h"
%End
public:
/** Constructor.
*/
QgsServerSettings();
/** Load settings according to current environment variables.
*/
void load();
/** Log a summary of settings curently loaded.
*/
void logSummary() const;
/** Returns the ini file loaded by QSetting.
* @return the path of the ini file or an empty string if none is loaded.
*/
QString iniFile() const;
/** Returns the maximum number of threads to use.
* @return the number of threads.
*/
int maxThreads() const;
/** Returns parallel rendering setting.
* @return true if parallel rendering is activated, false otherwise.
*/
bool parallelRendering() const;
/** Returns the log level.
* @return the log level.
*/
QgsMessageLog::MessageLevel logLevel() const;
/** Returns the log file.
* @return the path of the log file or an empty string if none is defined.
*/
QString logFile() const;
/** Returns the QGS project file to use.
* @return the path of the QGS project or an empty string if none is defined.
*/
QString projectFile() const;
/**
* Returns the maximum number of cached layers.
* @return the number of cached layers.
*/
int maxCacheLayers() const;
/** Returns the cache size.
* @return the cache size.
*/
qint64 cacheSize() const;
/** Returns the cache directory.
* @return the directory.
*/
QString cacheDirectory() const;
};

View File

@ -36,7 +36,7 @@ class QgsWCSServer: public QgsOWSServer
{
public:
/** Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
QgsWCSServer( const QString& configFilePath, QMap<QString, QString>& parameters, QgsWCSProjectParser* pp,
QgsWCSServer( const QString& configFilePath, const QgsServerSettings& settings, QMap<QString, QString>& parameters, QgsWCSProjectParser* pp,
QgsRequestHandler* rh, const QgsAccessControl* accessControl );
~QgsWCSServer();

View File

@ -59,7 +59,8 @@ class QgsWfsServer: public QgsOWSServer
{
public:
/** Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)*/
QgsWfsServer( const QString& configFilePath, QMap<QString, QString>& parameters, QgsWfsProjectParser* cp,
QgsWfsServer( const QString& configFilePath, const QgsServerSettings& settings,
QMap<QString, QString>& parameters, QgsWfsProjectParser* cp,
QgsRequestHandler* rh, const QgsAccessControl* accessControl );
~QgsWfsServer();

View File

@ -60,7 +60,7 @@ class QgsWmsServer: public QgsOWSServer
public:
/** Constructor. Does _NOT_ take ownership of
QgsConfigParser, QgsCapabilitiesCache*/
QgsWmsServer( const QString& configFilePath, QMap<QString, QString> &parameters, QgsWmsConfigParser* cp, QgsRequestHandler* rh, QgsCapabilitiesCache* capCache, const QgsAccessControl* accessControl );
QgsWmsServer( const QString& configFilePath, const QgsServerSettings& settings, QMap<QString, QString> &parameters, QgsWmsConfigParser* cp, QgsRequestHandler* rh, QgsCapabilitiesCache* capCache, const QgsAccessControl* accessControl );
~QgsWmsServer();
void executeRequest() override;

View File

@ -27,6 +27,7 @@
%Include qgswmsprojectparser.sip
%Include qgswfsprojectparser.sip
%Include qgsconfigcache.sip
%Include qgsserversettings.sip
%Include qgsserver.sip
%Include qgsserverrequest.sip

View File

@ -27,6 +27,7 @@ SET ( qgis_mapserv_SRCS
qgsowsserver.cpp
qgswfsserver.cpp
qgswcsserver.cpp
qgsserversettings.cpp
qgsmapserviceexception.cpp
qgsmslayercache.cpp
qgsmslayerbuilder.cpp
@ -75,6 +76,7 @@ SET (qgis_mapserv_MOC_HDRS
# qgshttptransaction.h
qgsmslayercache.h
qgsserverlogger.h
qgsserversettings.h
qgsserverstreamingdevice.h
)

View File

@ -22,12 +22,25 @@
#include <QStringList>
void QgsAccessControl::resolveFilterFeatures( const QList<QgsMapLayer*> &layers )
{
Q_FOREACH ( QgsMapLayer* l, layers )
{
if ( l->type() == QgsMapLayer::LayerType::VectorLayer )
{
const QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( l );
mFilterFeaturesExpressions[vl->id()] = resolveFilterFeatures( vl );
}
}
//! Filter the features of the layer
void QgsAccessControl::filterFeatures( const QgsVectorLayer* layer, QgsFeatureRequest& featureRequest ) const
mResolved = true;
}
QString QgsAccessControl::resolveFilterFeatures( const QgsVectorLayer* layer ) const
{
QStringList expressions = QStringList();
QgsAccessControlFilterMap::const_iterator acIterator;
for ( acIterator = mPluginsAccessControls->constBegin(); acIterator != mPluginsAccessControls->constEnd(); ++acIterator )
{
QString expression = acIterator.value()->layerFilterExpression( layer );
@ -36,9 +49,34 @@ void QgsAccessControl::filterFeatures( const QgsVectorLayer* layer, QgsFeatureRe
expressions.append( expression );
}
}
QString expression;
if ( !expressions.isEmpty() )
{
featureRequest.setFilterExpression( QStringLiteral( "((" ).append( expressions.join( QStringLiteral( ") AND (" ) ) ).append( "))" ) );
expression = QStringLiteral( "((" ).append( expressions.join( QStringLiteral( ") AND (" ) ) ).append( "))" );
}
return expression;
}
//! Filter the features of the layer
void QgsAccessControl::filterFeatures( const QgsVectorLayer* layer, QgsFeatureRequest& featureRequest ) const
{
QString expression;
if ( mResolved && mFilterFeaturesExpressions.keys().contains( layer->id() ) )
{
expression = mFilterFeaturesExpressions[layer->id()];
}
else
{
expression = resolveFilterFeatures( layer );
}
if ( !expression.isEmpty() )
{
featureRequest.setFilterExpression( expression );
}
}

View File

@ -40,12 +40,15 @@ class SERVER_EXPORT QgsAccessControl : public QgsFeatureFilterProvider
QgsAccessControl()
{
mPluginsAccessControls = new QgsAccessControlFilterMap();
mResolved = false;
}
//! Constructor
QgsAccessControl( const QgsAccessControl& copy )
{
mPluginsAccessControls = new QgsAccessControlFilterMap( *copy.mPluginsAccessControls );
mFilterFeaturesExpressions = copy.mFilterFeaturesExpressions;
mResolved = copy.mResolved;
}
@ -54,6 +57,11 @@ class SERVER_EXPORT QgsAccessControl : public QgsFeatureFilterProvider
delete mPluginsAccessControls;
}
/** Resolve features' filter of layers
* @param layers to filter
*/
void resolveFilterFeatures( const QList<QgsMapLayer*> &layers );
/** Filter the features of the layer
* @param layer the layer to control
* @param filterFeatures the request to fill
@ -122,8 +130,13 @@ class SERVER_EXPORT QgsAccessControl : public QgsFeatureFilterProvider
void registerAccessControl( QgsAccessControlFilter* accessControl, int priority = 0 );
private:
QString resolveFilterFeatures( const QgsVectorLayer* layer ) const;
//! The AccessControl plugins registry
QgsAccessControlFilterMap* mPluginsAccessControls;
QMap<QString, QString> mFilterFeaturesExpressions;
bool mResolved;
};
#endif

View File

@ -21,6 +21,7 @@
#include "qgsmaplayer.h"
#include "qgsvectorlayer.h"
#include "qgslogger.h"
#include "qgsserversettings.h"
#include <QFile>
QgsMSLayerCache* QgsMSLayerCache::instance()
@ -32,20 +33,8 @@ QgsMSLayerCache* QgsMSLayerCache::instance()
}
QgsMSLayerCache::QgsMSLayerCache()
: mProjectMaxLayers( 0 )
: mProjectMaxLayers( 100 )
{
mDefaultMaxLayers = 100;
//max layer from environment variable overrides default
char* maxLayerEnv = getenv( "MAX_CACHE_LAYERS" );
if ( maxLayerEnv )
{
bool conversionOk = false;
int maxLayerInt = QString( maxLayerEnv ).toInt( &conversionOk );
if ( conversionOk )
{
mDefaultMaxLayers = maxLayerInt;
}
}
QObject::connect( &mFileSystemWatcher, SIGNAL( fileChanged( const QString& ) ), this, SLOT( removeProjectFileLayers( const QString& ) ) );
}
@ -59,6 +48,11 @@ QgsMSLayerCache::~QgsMSLayerCache()
mEntries.clear();
}
void QgsMSLayerCache::setMaxCacheLayers( int maxCacheLayers )
{
mDefaultMaxLayers = maxCacheLayers;
}
void QgsMSLayerCache::insertLayer( const QString& url, const QString& layerName, QgsMapLayer* layer, const QString& configFile, const QList<QString>& tempFiles )
{
QgsMessageLog::logMessage( "Layer cache: insert Layer '" + layerName + "' configFile: " + configFile, QStringLiteral( "Server" ), QgsMessageLog::INFO );

View File

@ -56,6 +56,13 @@ class QgsMSLayerCache: public QObject
static QgsMSLayerCache* instance();
~QgsMSLayerCache();
/**
* Set the maximum number of layers in cache.
* @param maxCacheLayers the number of layers in cache
* @note added in QGIS 3.0
*/
void setMaxCacheLayers( int maxCacheLayers );
/** Inserts a new layer into the cash
@param url the layer datasource
@param layerName the layer name (to distinguish between different layers in a request using the same datasource

View File

@ -20,6 +20,7 @@
#include "qgsconfig.h"
#include "qgsrequesthandler.h"
#include "qgsserversettings.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsaccesscontrol.h"
#else
@ -35,11 +36,13 @@ class QgsOWSServer
public:
QgsOWSServer(
const QString& configFilePath
, const QgsServerSettings& settings
, const QMap<QString, QString>& parameters
, QgsRequestHandler* rh
, const QgsAccessControl* ac
, QgsAccessControl* ac
)
: mParameters( parameters )
: mSettings( settings )
, mParameters( parameters )
, mRequestHandler( rh )
, mConfigFilePath( configFilePath )
, mAccessControl( ac )
@ -57,12 +60,13 @@ class QgsOWSServer
QgsOWSServer() {}
protected:
QgsServerSettings mSettings;
QMap<QString, QString> mParameters;
QgsRequestHandler* mRequestHandler;
QString mConfigFilePath;
//! The access control helper
const QgsAccessControl* mAccessControl;
QgsAccessControl* mAccessControl;
#ifdef HAVE_SERVER_PYTHON_PLUGINS

View File

@ -21,6 +21,7 @@
//for CMAKE_INSTALL_PREFIX
#include "qgsconfig.h"
#include "qgsserver.h"
#include "qgsmslayercache.h"
#include "qgsmapsettings.h"
#include "qgsauthmanager.h"
@ -64,6 +65,7 @@ QgsCapabilitiesCache* QgsServer::sCapabilitiesCache = nullptr;
QgsServerInterfaceImpl* QgsServer::sServerInterface = nullptr;
// Initialization must run once for all servers
bool QgsServer::sInitialised = false;
QgsServerSettings QgsServer::sSettings;
QgsServiceRegistry QgsServer::sServiceRegistry;
@ -99,12 +101,8 @@ void QgsServer::setupNetworkAccessManager()
QSettings settings;
QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance();
QNetworkDiskCache *cache = new QNetworkDiskCache( nullptr );
QString cacheDirectory = settings.value( QStringLiteral( "cache/directory" ) ).toString();
if ( cacheDirectory.isEmpty() )
cacheDirectory = QgsApplication::qgisSettingsDirPath() + "cache";
qint64 cacheSize = settings.value( QStringLiteral( "cache/size" ), 50 * 1024 * 1024 ).toULongLong();
QgsMessageLog::logMessage( QStringLiteral( "setCacheDirectory: %1" ).arg( cacheDirectory ), QStringLiteral( "Server" ), QgsMessageLog::INFO );
QgsMessageLog::logMessage( QStringLiteral( "setMaximumCacheSize: %1" ).arg( cacheSize ), QStringLiteral( "Server" ), QgsMessageLog::INFO );
qint64 cacheSize = sSettings.cacheSize();
QString cacheDirectory = sSettings.cacheDirectory();
cache->setCacheDirectory( cacheDirectory );
cache->setMaximumCacheSize( cacheSize );
QgsMessageLog::logMessage( QStringLiteral( "cacheDirectory: %1" ).arg( cache->cacheDirectory() ), QStringLiteral( "Server" ), QgsMessageLog::INFO );
@ -214,7 +212,7 @@ void QgsServer::printRequestInfos()
QString QgsServer::configPath( const QString& defaultConfigPath, const QMap<QString, QString>& parameters )
{
QString cfPath( defaultConfigPath );
QString projectFile = getenv( "QGIS_PROJECT_FILE" );
QString projectFile = sSettings.projectFile();
if ( !projectFile.isEmpty() )
{
cfPath = projectFile;
@ -247,16 +245,6 @@ bool QgsServer::init( )
return false;
}
QgsServerLogger::instance();
QString optionsPath = getenv( "QGIS_OPTIONS_PATH" );
if ( !optionsPath.isEmpty() )
{
QgsMessageLog::logMessage( "Options PATH: " + optionsPath, QStringLiteral( "Server" ), QgsMessageLog::INFO );
QSettings::setDefaultFormat( QSettings::IniFormat );
QSettings::setPath( QSettings::IniFormat, QSettings::UserScope, optionsPath );
}
QCoreApplication::setOrganizationName( QgsApplication::QGIS_ORGANIZATION_NAME );
QCoreApplication::setOrganizationDomain( QgsApplication::QGIS_ORGANIZATION_DOMAIN );
QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME );
@ -277,6 +265,22 @@ bool QgsServer::init( )
QgsApplication::skipGdalDriver( "JP2ECW" );
#endif
// reload settings to take into account QCoreApplication and QgsApplication
// configuration
sSettings.load();
// init and configure logger
QgsServerLogger::instance();
QgsServerLogger::instance()->setLogLevel( sSettings.logLevel() );
QgsServerLogger::instance()->setLogFile( sSettings.logFile() );
// init and configure cache
QgsMSLayerCache::instance();
QgsMSLayerCache::instance()->setMaxCacheLayers( sSettings.maxCacheLayers() );
// log settings currently used
sSettings.logSummary();
setupNetworkAccessManager();
QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
@ -324,7 +328,7 @@ bool QgsServer::init( )
QgsEditorWidgetRegistry::initEditors();
sServerInterface = new QgsServerInterfaceImpl( sCapabilitiesCache, &sServiceRegistry );
sServerInterface = new QgsServerInterfaceImpl( sCapabilitiesCache, &sServiceRegistry, &sSettings );
// Load service module
QString modulePath = QgsApplication::libexecPath() + "server";
@ -345,6 +349,7 @@ void QgsServer::putenv( const QString &var, const QString &val )
#else
setenv( var.toStdString().c_str(), val.toStdString().c_str(), 1 );
#endif
sSettings.load( var );
}
/**
@ -394,7 +399,7 @@ void QgsServer::handleRequest( QgsServerRequest& request, QgsServerResponse& res
QMap<QString, QString> parameterMap = request.parameters();
printRequestParameters( parameterMap, logLevel );
const QgsAccessControl* accessControl = sServerInterface->accessControls();
QgsAccessControl* accessControl = sServerInterface->accessControls();
//Config file path
QString configFilePath = configPath( *sConfigFilePath, parameterMap );
@ -447,6 +452,7 @@ void QgsServer::handleRequest( QgsServerRequest& request, QgsServerResponse& res
{
QgsWCSServer wcsServer(
configFilePath
, sSettings
, parameterMap
, p
, &theRequestHandler
@ -469,6 +475,7 @@ void QgsServer::handleRequest( QgsServerRequest& request, QgsServerResponse& res
{
QgsWfsServer wfsServer(
configFilePath
, sSettings
, parameterMap
, p
, &theRequestHandler

View File

@ -35,6 +35,7 @@
#include "qgsmapsettings.h"
#include "qgsmessagelog.h"
#include "qgsserviceregistry.h"
#include "qgsserversettings.h"
#include "qgsserverplugins.h"
#include "qgsserverfilter.h"
#include "qgsserverinterfaceimpl.h"
@ -132,6 +133,7 @@ class SERVER_EXPORT QgsServer
//! service registry
static QgsServiceRegistry sServiceRegistry;
static QgsServerSettings sSettings;
};
#endif // QGSSERVER_H

View File

@ -25,6 +25,7 @@
#include "qgscapabilitiescache.h"
#include "qgsrequesthandler.h"
#include "qgsserverfilter.h"
#include "qgsserversettings.h"
#ifdef HAVE_SERVER_PYTHON_PLUGINS
#include "qgsaccesscontrolfilter.h"
#include "qgsaccesscontrol.h"
@ -111,7 +112,7 @@ class SERVER_EXPORT QgsServerInterface
virtual void registerAccessControl( QgsAccessControlFilter* accessControl, int priority = 0 ) = 0;
//! Gets the registred access control filters
virtual const QgsAccessControl* accessControls() const = 0;
virtual QgsAccessControl* accessControls() const = 0;
//! Return an enrironment variable, used to pass environment variables to python
virtual QString getEnv( const QString& name ) const = 0;
@ -146,6 +147,14 @@ class SERVER_EXPORT QgsServerInterface
*/
virtual QgsServiceRegistry* serviceRegistry() = 0;
/**
* Return the server settings
* @return QgsServerSettings
*
* @note not available in python bindings
*/
virtual QgsServerSettings* serverSettings() = 0;
private:
QString mConfigFilePath;
};

View File

@ -22,9 +22,10 @@
#include "qgsmslayercache.h"
//! Constructor
QgsServerInterfaceImpl::QgsServerInterfaceImpl( QgsCapabilitiesCache* capCache, QgsServiceRegistry* srvRegistry )
QgsServerInterfaceImpl::QgsServerInterfaceImpl( QgsCapabilitiesCache* capCache, QgsServiceRegistry* srvRegistry, QgsServerSettings* settings )
: mCapabilitiesCache( capCache )
, mServiceRegistry( srvRegistry )
, mServerSettings( settings )
{
mRequestHandler = nullptr;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
@ -100,3 +101,7 @@ QgsServiceRegistry* QgsServerInterfaceImpl::serviceRegistry()
return mServiceRegistry;
}
QgsServerSettings* QgsServerInterfaceImpl::serverSettings()
{
return mServerSettings;
}

View File

@ -36,7 +36,8 @@ class QgsServerInterfaceImpl : public QgsServerInterface
//! Constructor
explicit QgsServerInterfaceImpl( QgsCapabilitiesCache *capCache,
QgsServiceRegistry* srvRegistry );
QgsServiceRegistry* srvRegistry,
QgsServerSettings* serverSettings );
~QgsServerInterfaceImpl();
@ -55,7 +56,7 @@ class QgsServerInterfaceImpl : public QgsServerInterface
/** Gets the helper over all the registered access control filters
* @return the access control helper
*/
const QgsAccessControl* accessControls() const override { return mAccessControls; }
QgsAccessControl* accessControls() const override { return mAccessControls; }
QString getEnv( const QString& name ) const override;
QString configFilePath() override { return mConfigFilePath; }
void setConfigFilePath( const QString& configFilePath ) override;
@ -65,6 +66,8 @@ class QgsServerInterfaceImpl : public QgsServerInterface
QgsServiceRegistry* serviceRegistry() override;
QgsServerSettings* serverSettings() override;
private:
QString mConfigFilePath;
@ -73,6 +76,7 @@ class QgsServerInterfaceImpl : public QgsServerInterface
QgsCapabilitiesCache* mCapabilitiesCache;
QgsRequestHandler* mRequestHandler;
QgsServiceRegistry* mServiceRegistry;
QgsServerSettings* mServerSettings;
};
#endif // QGSSERVERINTERFACEIMPL_H

View File

@ -39,28 +39,33 @@ QgsServerLogger::QgsServerLogger()
: mLogFile( nullptr )
, mLogLevel( QgsMessageLog::NONE )
{
//logfile
QString filePath = getenv( "QGIS_SERVER_LOG_FILE" );
if ( filePath.isEmpty() )
return;
mLogFile.setFileName( filePath );
if ( mLogFile.open( QIODevice::Append ) )
{
mTextStream.setDevice( &mLogFile );
}
//log level
char* logLevelChar = getenv( "QGIS_SERVER_LOG_LEVEL" );
if ( logLevelChar )
{
mLogLevel = static_cast<QgsMessageLog::MessageLevel>( atoi( logLevelChar ) );
}
connect( QgsApplication::messageLog(), SIGNAL( messageReceived( QString, QString, QgsMessageLog::MessageLevel ) ), this,
SLOT( logMessage( QString, QString, QgsMessageLog::MessageLevel ) ) );
}
void QgsServerLogger::setLogLevel( QgsMessageLog::MessageLevel level )
{
mLogLevel = level;
}
void QgsServerLogger::setLogFile( const QString& f )
{
if ( ! f.isEmpty() )
{
if ( mLogFile.exists() )
{
mTextStream.flush();
mLogFile.close();
}
mLogFile.setFileName( f );
if ( mLogFile.open( QIODevice::Append ) )
{
mTextStream.setDevice( &mLogFile );
}
}
}
void QgsServerLogger::logMessage( const QString& message, const QString& tag, QgsMessageLog::MessageLevel level )
{
Q_UNUSED( tag );

View File

@ -38,9 +38,23 @@ class QgsServerLogger: public QObject
/**
* Get the current log level
* @return the log level
* @note added in QGIS 3.0
*/
QgsMessageLog::MessageLevel logLevel() const { return mLogLevel; }
/**
* Set the current log level
* @param level the log level
* @note added in QGIS 3.0
*/
void setLogLevel( QgsMessageLog::MessageLevel level );
/**
* Set the current log file
*/
void setLogFile( const QString& f );
public slots:
/**

View File

@ -0,0 +1,307 @@
/***************************************************************************
qgsserversettings.cpp
---------------------
begin : December 19, 2016
copyright : (C) 2016 by Paul Blottiere
email : paul dot blottiere at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsserversettings.h"
#include "qgsapplication.h"
#include <QSettings>
#include <iostream>
QgsServerSettings::QgsServerSettings()
{
load();
}
void QgsServerSettings::initSettings()
{
mSettings.clear();
// options path
const Setting sOptPath = { QgsServerSettingsEnv::QGIS_OPTIONS_PATH,
QgsServerSettingsEnv::DEFAULT_VALUE,
"Override the default path for user configuration",
"",
QVariant::String,
QVariant( "" ),
QVariant()
};
mSettings[ sOptPath.envVar ] = sOptPath;
// parallel rendering
const Setting sParRend = { QgsServerSettingsEnv::QGIS_SERVER_PARALLEL_RENDERING,
QgsServerSettingsEnv::DEFAULT_VALUE,
"Activate/Deactivate parallel rendering for WMS getMap request",
"/qgis/parallel_rendering",
QVariant::Bool,
QVariant( false ),
QVariant()
};
mSettings[ sParRend.envVar ] = sParRend;
// max threads
const Setting sMaxThreads = { QgsServerSettingsEnv::QGIS_SERVER_MAX_THREADS,
QgsServerSettingsEnv::DEFAULT_VALUE,
"Number of threads to use when parallel rendering is activated",
"/qgis/max_threads",
QVariant::Int,
QVariant( -1 ),
QVariant()
};
mSettings[ sMaxThreads.envVar ] = sMaxThreads;
// log level
const Setting sLogLevel = { QgsServerSettingsEnv::QGIS_SERVER_LOG_LEVEL,
QgsServerSettingsEnv::DEFAULT_VALUE,
"Log level",
"",
QVariant::Int,
QVariant( QgsMessageLog::NONE ),
QVariant()
};
mSettings[ sLogLevel.envVar ] = sLogLevel;
// log file
const Setting sLogFile = { QgsServerSettingsEnv::QGIS_SERVER_LOG_FILE,
QgsServerSettingsEnv::DEFAULT_VALUE,
"Log file",
"",
QVariant::String,
QVariant( "" ),
QVariant()
};
mSettings[ sLogFile.envVar ] = sLogFile;
// project file
const Setting sProject = { QgsServerSettingsEnv::QGIS_PROJECT_FILE,
QgsServerSettingsEnv::DEFAULT_VALUE,
"QGIS project file",
"",
QVariant::String,
QVariant( "" ),
QVariant()
};
mSettings[ sProject.envVar ] = sProject;
// max cache layers
const Setting sMaxCacheLayers = { QgsServerSettingsEnv::MAX_CACHE_LAYERS,
QgsServerSettingsEnv::DEFAULT_VALUE,
"Specify the maximum number of cached layers",
"",
QVariant::Int,
QVariant( 100 ),
QVariant()
};
mSettings[ sMaxCacheLayers.envVar ] = sMaxCacheLayers;
// cache directory
const Setting sCacheDir = { QgsServerSettingsEnv::QGIS_SERVER_CACHE_DIRECTORY,
QgsServerSettingsEnv::DEFAULT_VALUE,
"Specify the cache directory",
"/cache/directory",
QVariant::String,
QVariant( QgsApplication::qgisSettingsDirPath() + "cache" ),
QVariant()
};
mSettings[ sCacheDir.envVar ] = sCacheDir;
// cache size
const Setting sCacheSize = { QgsServerSettingsEnv::QGIS_SERVER_CACHE_SIZE,
QgsServerSettingsEnv::DEFAULT_VALUE,
"Specify the cache size",
"/cache/size",
QVariant::LongLong,
QVariant( 50*1024*1024 ),
QVariant()
};
mSettings[ sCacheSize.envVar ] = sCacheSize;
}
void QgsServerSettings::load()
{
// init settings each time to take into account QgsApplication and
// QCoreApplication configuration for some default values
initSettings();
// store environment variables
QMap<QgsServerSettingsEnv::EnvVar, QString> env = getEnv();
// load QSettings if QGIS_OPTIONS_PATH is defined
loadQSettings( env[ QgsServerSettingsEnv::QGIS_OPTIONS_PATH ] );
// prioritize values: 'env var' -> 'ini file' -> 'default value'
prioritize( env );
}
bool QgsServerSettings::load( const QString& envVarName )
{
bool rc( false );
const QMetaEnum metaEnum( QMetaEnum::fromType<QgsServerSettingsEnv::EnvVar>() );
const int value = metaEnum.keyToValue( envVarName.toStdString().c_str() );
if ( value >= 0 )
{
const QString envValue( getenv( envVarName.toStdString().c_str() ) );
prioritize( QMap<QgsServerSettingsEnv::EnvVar, QString> { {( QgsServerSettingsEnv::EnvVar ) value, envValue } } );
rc = true;
}
return rc;
}
QMap<QgsServerSettingsEnv::EnvVar, QString> QgsServerSettings::getEnv() const
{
QMap<QgsServerSettingsEnv::EnvVar, QString> env;
const QMetaEnum metaEnum( QMetaEnum::fromType<QgsServerSettingsEnv::EnvVar>() );
for ( int i = 0; i < metaEnum.keyCount(); i++ )
{
env[( QgsServerSettingsEnv::EnvVar ) metaEnum.value( i )] = getenv( metaEnum.key( i ) );
}
return env;
}
QVariant QgsServerSettings::value( QgsServerSettingsEnv::EnvVar envVar ) const
{
if ( mSettings[ envVar ].src == QgsServerSettingsEnv::DEFAULT_VALUE )
{
return mSettings[ envVar ].defaultVal;
}
else
{
return mSettings[ envVar ].val;
}
}
void QgsServerSettings::loadQSettings( const QString& envOptPath ) const
{
if ( ! envOptPath.isEmpty() )
{
QSettings::setDefaultFormat( QSettings::IniFormat );
QSettings::setPath( QSettings::IniFormat, QSettings::UserScope, envOptPath );
}
}
void QgsServerSettings::prioritize( const QMap<QgsServerSettingsEnv::EnvVar, QString>& env )
{
for ( QgsServerSettingsEnv::EnvVar e : env.keys() )
{
Setting s = mSettings[ e ];
QVariant varValue;
if ( ! env.value( e ).isEmpty() )
{
varValue.setValue( env.value( e ) );
}
if ( ! varValue.isNull() && varValue.canConvert( s.type ) )
{
s.val = varValue;
s.src = QgsServerSettingsEnv::ENVIRONMENT_VARIABLE;
}
else if ( ! s.iniKey.isEmpty() && QSettings().contains( s.iniKey ) && QSettings().value( s.iniKey ).canConvert( s.type ) )
{
s.val = QSettings().value( s.iniKey );
s.src = QgsServerSettingsEnv::INI_FILE;
}
else
{
s.val = QVariant();
s.src = QgsServerSettingsEnv::DEFAULT_VALUE;
}
// an empty string can be returned from QSettings. In this case, we want
// to use the default value
if ( s.type == QVariant::String && s.val.toString().isEmpty() )
{
s.val = QVariant();
s.src = QgsServerSettingsEnv::DEFAULT_VALUE;
}
mSettings[ e ] = s;
}
}
void QgsServerSettings::logSummary() const
{
const QMetaEnum metaEnumSrc( QMetaEnum::fromType<QgsServerSettingsEnv::Source>() );
const QMetaEnum metaEnumEnv( QMetaEnum::fromType<QgsServerSettingsEnv::EnvVar>() );
QgsMessageLog::logMessage( "Qgis Server Settings: ", "Server", QgsMessageLog::INFO );
for ( Setting s : mSettings )
{
const QString src = metaEnumSrc.valueToKey( s.src );
const QString var = metaEnumEnv.valueToKey( s.envVar );
const QString msg = " - " + var + " / '" + s.iniKey + "' (" + s.descr + "): '" + value( s.envVar ).toString() + "' (read from " + src + ")";
QgsMessageLog::logMessage( msg, "Server", QgsMessageLog::INFO );
}
if ( ! iniFile().isEmpty() )
{
const QString msg = "Ini file used to initialize settings: " + iniFile();
QgsMessageLog::logMessage( msg, "Server", QgsMessageLog::INFO );
}
}
// getter
QString QgsServerSettings::iniFile() const
{
return QSettings().fileName();
}
bool QgsServerSettings::parallelRendering() const
{
return value( QgsServerSettingsEnv::QGIS_SERVER_PARALLEL_RENDERING ).toBool();
}
int QgsServerSettings::maxThreads() const
{
return value( QgsServerSettingsEnv::QGIS_SERVER_MAX_THREADS ).toInt();
}
QString QgsServerSettings::logFile() const
{
return value( QgsServerSettingsEnv::QGIS_SERVER_LOG_FILE ).toString();
}
QgsMessageLog::MessageLevel QgsServerSettings::logLevel() const
{
return static_cast<QgsMessageLog::MessageLevel>( value( QgsServerSettingsEnv::QGIS_SERVER_LOG_LEVEL ).toInt() );
}
int QgsServerSettings::maxCacheLayers() const
{
return value( QgsServerSettingsEnv::MAX_CACHE_LAYERS ).toInt();
}
QString QgsServerSettings::projectFile() const
{
return value( QgsServerSettingsEnv::QGIS_PROJECT_FILE ).toString();
}
qint64 QgsServerSettings::cacheSize() const
{
return value( QgsServerSettingsEnv::QGIS_SERVER_CACHE_SIZE ).toLongLong();
}
QString QgsServerSettings::cacheDirectory() const
{
return value( QgsServerSettingsEnv::QGIS_SERVER_CACHE_DIRECTORY ).toString();
}

View File

@ -0,0 +1,154 @@
/***************************************************************************
qgsserversettings.h
-------------------
begin : December 19, 2016
copyright : (C) 2016 by Paul Blottiere
email : paul dot blottiere at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSERVERSETTINGS_H
#define QGSSERVERSETTINGS_H
#include <QObject>
#include <QMetaEnum>
#include "qgsmessagelog.h"
#include "qgis_server.h"
/**
* QgsServerSettingsEnv provides some enum describing the environment
* currently supported for configuration.
* @note added in QGIS 3.0
*/
class QgsServerSettingsEnv : public QObject
{
Q_OBJECT
public:
enum Source
{
DEFAULT_VALUE,
ENVIRONMENT_VARIABLE,
INI_FILE
};
Q_ENUM( Source )
enum EnvVar
{
QGIS_OPTIONS_PATH,
QGIS_SERVER_PARALLEL_RENDERING,
QGIS_SERVER_MAX_THREADS,
QGIS_SERVER_LOG_LEVEL,
QGIS_SERVER_LOG_FILE,
QGIS_PROJECT_FILE,
MAX_CACHE_LAYERS,
QGIS_SERVER_CACHE_DIRECTORY,
QGIS_SERVER_CACHE_SIZE
};
Q_ENUM( EnvVar )
};
/** \ingroup server
* QgsServerSettings provides a way to retrieve settings by prioritizing
* according to environment variables, ini file and default values.
* @note added in QGIS 3.0
*/
class SERVER_EXPORT QgsServerSettings
{
public:
struct Setting
{
QgsServerSettingsEnv::EnvVar envVar;
QgsServerSettingsEnv::Source src;
QString descr;
QString iniKey;
QVariant::Type type;
QVariant defaultVal;
QVariant val;
};
/** Constructor.
*/
QgsServerSettings();
/** Load settings according to current environment variables.
*/
void load();
/** Load setting for a specific environment variable name.
* @return true if loading is successful, false in case of an invalid name.
*/
bool load( const QString& envVarName );
/** Log a summary of settings curently loaded.
*/
void logSummary() const;
/** Returns the ini file loaded by QSetting.
* @return the path of the ini file or an empty string if none is loaded.
*/
QString iniFile() const;
/** Returns parallel rendering setting.
* @return true if parallel rendering is activated, false otherwise.
*/
bool parallelRendering() const;
/** Returns the maximum number of threads to use.
* @return the number of threads.
*/
int maxThreads() const;
/**
* Returns the maximum number of cached layers.
* @return the number of cached layers.
*/
int maxCacheLayers() const;
/** Returns the log level.
* @return the log level.
*/
QgsMessageLog::MessageLevel logLevel() const;
/** Returns the QGS project file to use.
* @return the path of the QGS project or an empty string if none is defined.
*/
QString projectFile() const;
/** Returns the log file.
* @return the path of the log file or an empty string if none is defined.
*/
QString logFile() const;
/** Returns the cache size.
* @return the cache size.
*/
qint64 cacheSize() const;
/** Returns the cache directory.
* @return the directory.
*/
QString cacheDirectory() const;
private:
void initSettings();
QVariant value( QgsServerSettingsEnv::EnvVar envVar ) const;
QMap<QgsServerSettingsEnv::EnvVar, QString> getEnv() const;
void loadQSettings( const QString& envOptPath ) const;
void prioritize( const QMap<QgsServerSettingsEnv::EnvVar, QString>& env );
QMap< QgsServerSettingsEnv::EnvVar, Setting > mSettings;
};
#endif

View File

@ -40,13 +40,15 @@ static const QString OGC_NAMESPACE = QStringLiteral( "http://www.opengis.net/ogc
QgsWCSServer::QgsWCSServer(
const QString& configFilePath
, const QgsServerSettings& settings
, QMap<QString, QString> &parameters
, QgsWCSProjectParser* pp
, QgsRequestHandler* rh
, const QgsAccessControl* accessControl
, QgsAccessControl* accessControl
)
: QgsOWSServer(
configFilePath
, settings
, parameters
, rh
, accessControl
@ -60,6 +62,7 @@ QgsWCSServer::QgsWCSServer(
QgsWCSServer::QgsWCSServer()
: QgsOWSServer(
QString()
, QgsServerSettings()
, QMap<QString, QString>()
, nullptr
, nullptr

View File

@ -38,10 +38,11 @@ class QgsWCSServer: public QgsOWSServer
//! Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)
QgsWCSServer(
const QString& configFilePath
, const QgsServerSettings& settings
, QMap<QString, QString>& parameters
, QgsWCSProjectParser* pp
, QgsRequestHandler* rh
, const QgsAccessControl* accessControl
, QgsAccessControl* accessControl
);
void executeRequest() override;

View File

@ -64,13 +64,15 @@ static const QString QGS_NAMESPACE = QStringLiteral( "http://www.qgis.org/gml" )
QgsWfsServer::QgsWfsServer(
const QString& configFilePath
, const QgsServerSettings& settings
, QMap<QString, QString> &parameters
, QgsWfsProjectParser* cp
, QgsRequestHandler* rh
, const QgsAccessControl* accessControl
, QgsAccessControl* accessControl
)
: QgsOWSServer(
configFilePath
, settings
, parameters
, rh
, accessControl
@ -83,6 +85,7 @@ QgsWfsServer::QgsWfsServer(
QgsWfsServer::QgsWfsServer()
: QgsOWSServer(
QString()
, QgsServerSettings()
, QMap<QString, QString>()
, nullptr
, nullptr

View File

@ -59,10 +59,11 @@ class QgsWfsServer: public QgsOWSServer
//! Constructor. Takes parameter map and a pointer to a renderer object (does not take ownership)
QgsWfsServer(
const QString& configFilePath
, const QgsServerSettings& settings
, QMap<QString, QString>& parameters
, QgsWfsProjectParser* cp
, QgsRequestHandler* rh
, const QgsAccessControl* accessControl
, QgsAccessControl* accessControl
);
void executeRequest() override;

View File

@ -17,6 +17,7 @@ SET (wms_SRCS
qgswmsgetstyle.cpp
qgswmsgetstyles.cpp
qgswmsservertransitional.cpp
qgsmaprendererjobproxy.cpp
qgsfilterrestorer.cpp
)
@ -40,14 +41,14 @@ INCLUDE_DIRECTORIES(
${CMAKE_BINARY_DIR}/src/analysis
${CMAKE_BINARY_DIR}/src/server
${CMAKE_CURRENT_BINARY_DIR}
../../../core
../../../core
../../../core/dxf
../../../core/geometry
../../../core/geometry
../../../core/raster
../../../core/symbology-ng
../../../core/composer
../../../core/layertree
../../../gui
../../../gui
../../../gui/editorwidgets
../../../gui/editorwidgets/core
../..

View File

@ -122,7 +122,9 @@ namespace QgsWms
QMap<QString, QString> formatOptionsMap = parseFormatOptions( params.value( QStringLiteral( "FORMAT_OPTIONS" ) ) );
QgsWmsServer server( serverIface->configFilePath(), params,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(),
params,
configParser,
serverIface->accessControls() );

View File

@ -0,0 +1,80 @@
/***************************************************************************
qgsmaprendererjobproxy.cpp
--------------------------
begin : January 2017
copyright : (C) 2017 by Paul Blottiere
email : paul dot blottiere at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsmaprendererjobproxy.h"
#include "qgsmessagelog.h"
#include "qgsmaprendererparalleljob.h"
#include "qgsmaprenderercustompainterjob.h"
namespace QgsWms
{
QgsMapRendererJobProxy::QgsMapRendererJobProxy(
bool parallelRendering
, int maxThreads
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, QgsAccessControl* accessControl
#endif
)
:
mParallelRendering( parallelRendering )
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, mAccessControl( accessControl )
#endif
{
if ( mParallelRendering )
{
QgsApplication::setMaxThreads( maxThreads );
QgsMessageLog::logMessage( QStringLiteral( "Parallel rendering activated with %1 threads" ).arg( maxThreads ), QStringLiteral( "server" ), QgsMessageLog::INFO );
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "Parallel rendering deactivated" ), QStringLiteral( "server" ), QgsMessageLog::INFO );
}
}
void QgsMapRendererJobProxy::render( const QgsMapSettings& mapSettings, QImage* image )
{
if ( mParallelRendering )
{
QgsMapRendererParallelJob renderJob( mapSettings );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
renderJob.setFeatureFilterProvider( mAccessControl );
#endif
renderJob.start();
renderJob.waitForFinished();
*image = renderJob.renderedImage();
mPainter.reset( new QPainter( image ) );
}
else
{
mPainter.reset( new QPainter( image ) );
QgsMapRendererCustomPainterJob renderJob( mapSettings, mPainter.data() );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
renderJob.setFeatureFilterProvider( mAccessControl );
#endif
renderJob.renderSynchronously();
}
}
QPainter* QgsMapRendererJobProxy::takePainter()
{
return mPainter.take();
}
} // namespace qgsws

View File

@ -0,0 +1,68 @@
/***************************************************************************
qgsmaprendererjobproxy.h
------------------------
begin : January 2017
copyright : (C) 2017 by Paul Blottiere
email : paul dot blottiere at oslandia dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSMAPRENDERERJOBPROXY_H
#define QGSMAPRENDERERJOBPROXY_H
#include "qgsmapsettings.h"
#include "qgsaccesscontrol.h"
namespace QgsWms
{
/** \ingroup server
* thiss class provides a proxy for sequential or parallel map render job by
* reading qsettings.
* @note added in QGIS 3.0
*/
class QgsMapRendererJobProxy
{
public:
/** Constructor.
* @param accessControl Does not take ownership of QgsAccessControl
*/
QgsMapRendererJobProxy(
bool parallelRendering
, int maxThreads
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, QgsAccessControl* accessControl
#endif
);
/** Sequential or parallel map rendering according to qsettings.
* @param mapSettings passed to MapRendererJob
* @param the rendered image
*/
void render( const QgsMapSettings& mapSettings, QImage* image );
/** Take ownership of the painter used for rendering.
* @return painter
*/
QPainter* takePainter();
private:
bool mParallelRendering;
#ifdef HAVE_SERVER_PYTHON_PLUGINS
QgsAccessControl* mAccessControl;
#endif
QScopedPointer<QPainter> mPainter;
};
}
#endif

View File

@ -32,7 +32,9 @@ namespace QgsWms
QgsServerRequest::Parameters params = request.parameters();
try
{
QgsWmsServer server( serverIface->configFilePath(), params,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(),
params,
getConfigParser( serverIface ),
serverIface->accessControls() );
QDomDocument doc = server.describeLayer();

View File

@ -31,7 +31,8 @@ namespace QgsWms
{
QgsServerRequest::Parameters params = request.parameters();
QString configFilePath = serverIface->configFilePath();
const QgsAccessControl* accessControl = serverIface->accessControls();
QgsServerSettings* serverSettings = serverIface->serverSettings();
QgsAccessControl* accessControl = serverIface->accessControls();
QgsCapabilitiesCache* capabilitiesCache = serverIface->capabilitiesCache();
QStringList cacheKeyList;
@ -50,7 +51,9 @@ namespace QgsWms
QDomDocument doc;
try
{
QgsWmsServer server( configFilePath, params,
QgsWmsServer server( configFilePath,
*serverSettings,
params,
getConfigParser( serverIface ),
accessControl );
doc = server.getCapabilities( version, projectSettings );

View File

@ -32,7 +32,9 @@ namespace QgsWms
try
{
Q_UNUSED( version );
QgsWmsServer server( serverIface->configFilePath(), params,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(),
params,
getConfigParser( serverIface ),
serverIface->accessControls() );
QDomDocument doc = server.getContext();

View File

@ -161,7 +161,8 @@ namespace QgsWms
{
Q_UNUSED( version );
QgsServerRequest::Parameters params = request.parameters();
QgsWmsServer server( serverIface->configFilePath(), params,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(), params,
getConfigParser( serverIface ),
serverIface->accessControls() );
try

View File

@ -33,8 +33,8 @@ namespace QgsWms
QgsServerRequest::Parameters params = request.parameters();
QgsWmsConfigParser* parser = getConfigParser( serverIface );
QgsWmsServer server( serverIface->configFilePath(), params, parser,
serverIface->accessControls() );
QgsWmsServer server( serverIface->configFilePath(), *serverIface->serverSettings(),
params, parser, serverIface->accessControls() );
try
{
QScopedPointer<QImage> result( server.getLegendGraphics() );

View File

@ -34,7 +34,8 @@ namespace QgsWms
QgsServerRequest::Parameters params = request.parameters();
QgsWmsConfigParser* parser = getConfigParser( serverIface );
QgsWmsServer server( serverIface->configFilePath(), params, parser,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(), params, parser,
serverIface->accessControls() );
try
{

View File

@ -31,7 +31,8 @@ namespace QgsWms
try
{
Q_UNUSED( version );
QgsWmsServer server( serverIface->configFilePath(), params,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(), params,
getConfigParser( serverIface ),
serverIface->accessControls() );

View File

@ -32,7 +32,8 @@ namespace QgsWms
QgsServerRequest::Parameters params = request.parameters();
try
{
QgsWmsServer server( serverIface->configFilePath(), params,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(), params,
getConfigParser( serverIface ),
serverIface->accessControls() );
QDomDocument doc = server.getSchemaExtension();

View File

@ -33,7 +33,8 @@ namespace QgsWms
try
{
Q_UNUSED( version );
QgsWmsServer server( serverIface->configFilePath(), params,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(), params,
getConfigParser( serverIface ),
serverIface->accessControls() );
QDomDocument doc = server.getStyle();

View File

@ -30,7 +30,8 @@ namespace QgsWms
{
Q_UNUSED( version );
QgsServerRequest::Parameters params = request.parameters();
QgsWmsServer server( serverIface->configFilePath(), params,
QgsWmsServer server( serverIface->configFilePath(),
*serverIface->serverSettings(), params,
getConfigParser( serverIface ),
serverIface->accessControls() );
try

View File

@ -52,7 +52,7 @@
#include "qgseditorwidgetregistry.h"
#include "qgsaccesscontrol.h"
#include "qgsfeaturerequest.h"
#include "qgsmaprenderercustompainterjob.h"
#include "qgsmaprendererjobproxy.h"
#include <QImage>
#include <QPainter>
@ -74,9 +74,10 @@ namespace QgsWms
QgsWmsServer::QgsWmsServer(
const QString& configFilePath
, const QgsServerSettings& settings
, QMap<QString, QString> &parameters
, QgsWmsConfigParser* cp
, const QgsAccessControl* accessControl
, QgsAccessControl* accessControl
)
: mParameters( parameters )
, mOwnsConfigParser( false )
@ -85,6 +86,7 @@ namespace QgsWms
, mConfigParser( cp )
, mConfigFilePath( configFilePath )
, mAccessControl( accessControl )
, mSettings( settings )
{
}
@ -685,10 +687,9 @@ namespace QgsWms
}
void QgsWmsServer::runHitTest( const QgsMapSettings& mapSettings, QPainter* painter, HitTest& hitTest )
void QgsWmsServer::runHitTest( const QgsMapSettings& mapSettings, HitTest& hitTest )
{
QgsRenderContext context = QgsRenderContext::fromMapSettings( mapSettings );
context.setPainter( painter ); // we are not going to draw anything, but we still need a working painter
Q_FOREACH ( const QString& layerID, mapSettings.layerIds() )
{
@ -1099,9 +1100,6 @@ namespace QgsWms
QStringList layersList, stylesList, layerIdList;
QImage* theImage = initializeRendering( layersList, stylesList, layerIdList, mapSettings );
QPainter thePainter( theImage );
thePainter.setRenderHint( QPainter::Antialiasing ); //make it look nicer
QStringList layerSetIds = mapSettings.layerIds();
QStringList highlightLayersId = QgsWmsConfigParser::addHighlightLayers( mParameters, layerSetIds );
@ -1142,36 +1140,41 @@ namespace QgsWms
applyOpacities( layersList, bkVectorRenderers, bkRasterRenderers, labelTransparencies, labelBufferTransparencies );
QScopedPointer<QPainter> painter;
if ( hitTest )
runHitTest( mapSettings, &thePainter, *hitTest );
{
runHitTest( mapSettings, *hitTest );
painter.reset( new QPainter() );
}
else
{
QgsMapRendererCustomPainterJob renderJob( mapSettings, &thePainter );
#ifdef HAVE_SERVER_PYTHON_PLUGINS
renderJob.setFeatureFilterProvider( mAccessControl );
mAccessControl->resolveFilterFeatures( mapSettings.layers() );
#endif
renderJob.renderSynchronously();
QgsMapRendererJobProxy renderJob( mSettings.parallelRendering(),
mSettings.maxThreads()
#ifdef HAVE_SERVER_PYTHON_PLUGINS
, mAccessControl
#endif
);
renderJob.render( mapSettings, theImage );
painter.reset( renderJob.takePainter() );
}
if ( mConfigParser )
{
//draw configuration format specific overlay items
mConfigParser->drawOverlays( &thePainter, theImage->dotsPerMeterX() / 1000.0 * 25.4, theImage->width(), theImage->height() );
mConfigParser->drawOverlays( painter.data(), theImage->dotsPerMeterX() / 1000.0 * 25.4, theImage->width(), theImage->height() );
}
restoreOpacities( bkVectorRenderers, bkRasterRenderers, labelTransparencies, labelBufferTransparencies );
clearFeatureSelections( selectedLayerIdList );
QgsWmsConfigParser::removeHighlightLayers( highlightLayersId );
// QgsMessageLog::logMessage( "clearing filters" );
if ( !hitTest )
QgsProject::instance()->removeAllMapLayers();
//#ifdef QGISDEBUG
// theImage->save( QDir::tempPath() + QDir::separator() + "lastrender.png" );
//#endif
thePainter.end();
painter->end();
//test if width / height ratio of image is the same as the ratio of WIDTH / HEIGHT parameters. If not, the image has to be scaled (required by WMS spec)
int widthParam = mParameters.value( "WIDTH", "0" ).toInt();

View File

@ -19,6 +19,7 @@
#define QGSWMSSERVER_H
#include "qgswmsconfigparser.h"
#include "qgsserversettings.h"
#include <QDomDocument>
#include <QMap>
#include <QPair>
@ -67,9 +68,10 @@ namespace QgsWms
QgsConfigParser and QgsCapabilitiesCache*/
QgsWmsServer(
const QString& configFilePath
, const QgsServerSettings& settings
, QMap<QString, QString> &parameters
, QgsWmsConfigParser* cp
, const QgsAccessControl* accessControl
, QgsAccessControl* accessControl
);
~QgsWmsServer();
@ -190,7 +192,7 @@ namespace QgsWms
QStringList layerSet( const QStringList& layersList, const QStringList& stylesList, const QgsCoordinateReferenceSystem& destCRS, double scaleDenominator = -1 ) const;
//! Record which symbols would be used if the map was in the current configuration of renderer. This is useful for content-based legend
void runHitTest( const QgsMapSettings& mapSettings, QPainter* painter, HitTest& hitTest );
void runHitTest( const QgsMapSettings& mapSettings, HitTest& hitTest );
//! Record which symbols within one layer would be rendered with the given renderer context
void runHitTestLayer( QgsVectorLayer* vl, SymbolSet& usedSymbols, QgsRenderContext& context );
@ -272,7 +274,9 @@ namespace QgsWms
QgsWmsConfigParser* mConfigParser;
QString mConfigFilePath;
//! The access control helper
const QgsAccessControl* mAccessControl;
QgsAccessControl* mAccessControl;
QgsServerSettings mSettings;
public:
QDomElement createFeatureGML(

View File

@ -166,6 +166,7 @@ ENDIF (WITH_APIDOC)
IF (WITH_SERVER)
ADD_PYTHON_TEST(PyQgsServer test_qgsserver.py)
ADD_PYTHON_TEST(PyQgsServerSettings test_qgsserver_settings.py)
ADD_PYTHON_TEST(PyQgsServerAccessControl test_qgsserver_accesscontrol.py)
ADD_PYTHON_TEST(PyQgsServerWFST test_qgsserver_wfst.py)
ADD_PYTHON_TEST(PyQgsOfflineEditingWFS test_offline_editing_wfs.py)

View File

@ -0,0 +1,214 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsServerSettings.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = 'Paul Blottiere'
__date__ = '20/12/2016'
__copyright__ = 'Copyright 2016, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import os
from qgis.PyQt.QtCore import QCoreApplication
from utilities import unitTestDataPath
from qgis.testing import unittest
from qgis.core import QgsApplication
from qgis.server import QgsServerSettings
class TestQgsServerSettings(unittest.TestCase):
def setUp(self):
self.settings = QgsServerSettings()
self.testdata_path = unitTestDataPath("qgis_server_settings")
def tearDown(self):
pass
def test_env_parallel_rendering(self):
env = "QGIS_SERVER_PARALLEL_RENDERING"
# test parallel rendering value from environment variable
os.environ[env] = "1"
self.settings.load()
self.assertTrue(self.settings.parallelRendering())
os.environ.pop(env)
os.environ[env] = "0"
self.settings.load()
self.assertFalse(self.settings.parallelRendering())
os.environ.pop(env)
def test_env_log_level(self):
env = "QGIS_SERVER_LOG_LEVEL"
# test log level value from environment variable
os.environ[env] = "3"
self.settings.load()
self.assertEqual(self.settings.logLevel(), 3)
os.environ.pop(env)
os.environ[env] = "1"
self.settings.load()
self.assertEqual(self.settings.logLevel(), 1)
os.environ.pop(env)
def test_env_log_file(self):
env = "QGIS_SERVER_LOG_FILE"
# test parallel rendering value from environment variable
os.environ[env] = "/tmp/qgisserv.log"
self.settings.load()
self.assertEqual(self.settings.logFile(), "/tmp/qgisserv.log")
os.environ.pop(env)
os.environ[env] = "/tmp/qserv.log"
self.settings.load()
self.assertEqual(self.settings.logFile(), "/tmp/qserv.log")
os.environ.pop(env)
def test_env_project_file(self):
env = "QGIS_PROJECT_FILE"
# test parallel rendering value from environment variable
os.environ[env] = "/tmp/myproject.qgs"
self.settings.load()
self.assertEqual(self.settings.projectFile(), "/tmp/myproject.qgs")
os.environ.pop(env)
os.environ[env] = "/tmp/myproject2.qgs"
self.settings.load()
self.assertEqual(self.settings.projectFile(), "/tmp/myproject2.qgs")
os.environ.pop(env)
def test_env_max_cache_layers(self):
env = "MAX_CACHE_LAYERS"
# test parallel rendering value from environment variable
os.environ[env] = "3"
self.settings.load()
self.assertEqual(self.settings.maxCacheLayers(), 3)
os.environ.pop(env)
os.environ[env] = "100"
self.settings.load()
self.assertEqual(self.settings.maxCacheLayers(), 100)
os.environ.pop(env)
def test_env_max_threads(self):
env = "QGIS_SERVER_MAX_THREADS"
# test parallel rendering value from environment variable
os.environ[env] = "3"
self.settings.load()
self.assertEqual(self.settings.maxThreads(), 3)
os.environ.pop(env)
os.environ[env] = "5"
self.settings.load()
self.assertEqual(self.settings.maxThreads(), 5)
os.environ.pop(env)
def test_env_cache_size(self):
env = "QGIS_SERVER_CACHE_SIZE"
self.assertEqual(self.settings.cacheSize(), 50 * 1024 * 1024)
os.environ[env] = "1024"
self.settings.load()
self.assertEqual(self.settings.cacheSize(), 1024)
os.environ.pop(env)
def test_env_cache_directory(self):
env = "QGIS_SERVER_CACHE_DIRECTORY"
os.environ[env] = "/tmp/fake"
self.settings.load()
self.assertEqual(self.settings.cacheDirectory(), "/tmp/fake")
os.environ.pop(env)
def test_priority(self):
env = "QGIS_OPTIONS_PATH"
dpath = "conf0"
ini = "{0}.ini".format(os.path.join(self.testdata_path, dpath))
QCoreApplication.setOrganizationName(dpath)
# load settings
os.environ[env] = self.testdata_path
self.settings.load()
# test conf
self.assertTrue(self.settings.parallelRendering())
self.assertEqual(self.settings.maxThreads(), 3)
# set environment variables and test priority
env_pr = "QGIS_SERVER_PARALLEL_RENDERING"
os.environ[env_pr] = "0"
env_mt = "QGIS_SERVER_MAX_THREADS"
os.environ[env_mt] = "5"
self.settings.load()
self.assertFalse(self.settings.parallelRendering())
self.assertEqual(self.settings.maxThreads(), 5)
# clear environment
os.environ.pop(env)
os.environ.pop(env_pr)
os.environ.pop(env_mt)
def test_options_path_conf0(self):
env = "QGIS_OPTIONS_PATH"
dpath = "conf0"
ini = "{0}.ini".format(os.path.join(self.testdata_path, dpath))
QCoreApplication.setOrganizationName(dpath)
# load settings
os.environ[env] = self.testdata_path
self.settings.load()
# test ini file
self.assertEqual(ini, self.settings.iniFile())
# test conf
self.assertTrue(self.settings.parallelRendering())
self.assertEqual(self.settings.maxThreads(), 3)
self.assertEqual(self.settings.cacheSize(), 52428800)
# default value when an empty string is indicated in ini file
self.assertEqual(self.settings.cacheDirectory(), "cache")
# clear environment
os.environ.pop(env)
def test_options_path_conf1(self):
env = "QGIS_OPTIONS_PATH"
dpath = "conf1"
ini = "{0}.ini".format(os.path.join(self.testdata_path, dpath))
QCoreApplication.setOrganizationName(dpath)
# load settings
os.environ[env] = self.testdata_path
self.settings.load()
# test ini file
self.assertEqual(ini, self.settings.iniFile())
# test conf
self.assertFalse(self.settings.parallelRendering())
self.assertEqual(self.settings.maxThreads(), 5)
self.assertEqual(self.settings.cacheSize(), 52428800)
self.assertEqual(self.settings.cacheDirectory(), "/tmp/mycache")
# clear environment
os.environ.pop(env)
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,7 @@
[cache]
directory=
size=@Variant(\0\0\0\x81\0\0\0\0\x3 \0\0)
[qgis]
parallel_rendering=true
max_threads=3

View File

@ -0,0 +1,7 @@
[cache]
directory=/tmp/mycache
size=@Variant(\0\0\0\x81\0\0\0\0\x3 \0\0)
[qgis]
parallel_rendering=false
max_threads=5