mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-12 00:02:25 -04:00
1928 lines
62 KiB
C++
1928 lines
62 KiB
C++
/***************************************************************************
|
|
qgswmsserver.cpp
|
|
-------------------
|
|
begin : May 14, 2006
|
|
copyright : (C) 2006 by Marco Hugentobler & Ionut Iosifescu Enescu
|
|
email : marco dot hugentobler at karto dot baug dot ethz dot ch
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* *
|
|
* 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 "qgswmsserver.h"
|
|
#include "qgsconfigparser.h"
|
|
#include "qgsepsgcache.h"
|
|
#include "qgsfield.h"
|
|
#include "qgsgeometry.h"
|
|
#include "qgsmaplayer.h"
|
|
#include "qgsmaplayerregistry.h"
|
|
#include "qgsmaprenderer.h"
|
|
#include "qgsmaptopixel.h"
|
|
#include "qgspallabeling.h"
|
|
#include "qgsproject.h"
|
|
#include "qgsrasterlayer.h"
|
|
#include "qgsscalecalculator.h"
|
|
#include "qgscoordinatereferencesystem.h"
|
|
#include "qgsvectordataprovider.h"
|
|
#include "qgsvectorlayer.h"
|
|
#include "qgslogger.h"
|
|
#include "qgsmapserviceexception.h"
|
|
#include "qgssldparser.h"
|
|
#include "qgssymbol.h"
|
|
#include "qgssymbolv2.h"
|
|
#include "qgsrenderer.h"
|
|
#include "qgslegendmodel.h"
|
|
#include "qgscomposerlegenditem.h"
|
|
#include "qgslogger.h"
|
|
#include <QImage>
|
|
#include <QPainter>
|
|
#include <QStringList>
|
|
#include <QTextStream>
|
|
#include <QDir>
|
|
|
|
//for printing
|
|
#include "qgscomposition.h"
|
|
#include <QBuffer>
|
|
#include <QPrinter>
|
|
#include <QSvgGenerator>
|
|
#include <QUrl>
|
|
#include <QPaintEngine>
|
|
|
|
QgsWMSServer::QgsWMSServer( std::map<QString, QString> parameters, QgsMapRenderer* renderer )
|
|
: mParameterMap( parameters )
|
|
, mConfigParser( 0 )
|
|
, mMapRenderer( renderer )
|
|
{
|
|
}
|
|
|
|
QgsWMSServer::~QgsWMSServer()
|
|
{
|
|
}
|
|
|
|
QgsWMSServer::QgsWMSServer()
|
|
{
|
|
}
|
|
|
|
QDomDocument QgsWMSServer::getCapabilities()
|
|
{
|
|
QgsDebugMsg( "Entering." );
|
|
QDomDocument doc;
|
|
//wms:WMS_Capabilities element
|
|
QDomElement wmsCapabilitiesElement = doc.createElement( "WMS_Capabilities"/*wms:WMS_Capabilities*/ );
|
|
wmsCapabilitiesElement.setAttribute( "xmlns", "http://www.opengis.net/wms" );
|
|
wmsCapabilitiesElement.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
|
|
wmsCapabilitiesElement.setAttribute( "xsi:schemaLocation", "http://www.opengis.net/wms http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd" );
|
|
wmsCapabilitiesElement.setAttribute( "version", "1.3.0" );
|
|
doc.appendChild( wmsCapabilitiesElement );
|
|
|
|
if ( mConfigParser )
|
|
{
|
|
mConfigParser->serviceCapabilities( wmsCapabilitiesElement, doc );
|
|
}
|
|
|
|
//wms:Capability element
|
|
QDomElement capabilityElement = doc.createElement( "Capability"/*wms:Capability*/ );
|
|
wmsCapabilitiesElement.appendChild( capabilityElement );
|
|
//wms:Request element
|
|
QDomElement requestElement = doc.createElement( "Request"/*wms:Request*/ );
|
|
capabilityElement.appendChild( requestElement );
|
|
//wms:GetCapabilities
|
|
QDomElement getCapabilitiesElement = doc.createElement( "GetCapabilities"/*wms:GetCapabilities*/ );
|
|
requestElement.appendChild( getCapabilitiesElement );
|
|
QDomElement capabilitiesFormatElement = doc.createElement( "Format" );/*wms:Format*/
|
|
getCapabilitiesElement.appendChild( capabilitiesFormatElement );
|
|
QDomText capabilitiesFormatText = doc.createTextNode( "text/xml" );
|
|
capabilitiesFormatElement.appendChild( capabilitiesFormatText );
|
|
|
|
QDomElement dcpTypeElement = doc.createElement( "DCPType"/*wms:DCPType*/ );
|
|
getCapabilitiesElement.appendChild( dcpTypeElement );
|
|
QDomElement httpElement = doc.createElement( "HTTP"/*wms:HTTP*/ );
|
|
dcpTypeElement.appendChild( httpElement );
|
|
|
|
//Prepare url
|
|
//Some client requests already have http://<SERVER_NAME> in the REQUEST_URI variable
|
|
QString hrefString;
|
|
QString requestUrl = getenv( "REQUEST_URI" );
|
|
QUrl mapUrl( requestUrl );
|
|
mapUrl.setHost( QString( getenv( "SERVER_NAME" ) ) );
|
|
|
|
//Add non-default ports to url
|
|
QString portString = getenv( "SERVER_PORT" );
|
|
if ( !portString.isEmpty() )
|
|
{
|
|
bool portOk;
|
|
int portNumber = portString.toInt( &portOk );
|
|
if ( portOk )
|
|
{
|
|
if ( portNumber != 80 )
|
|
{
|
|
mapUrl.setPort( portNumber );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( QString( getenv( "HTTPS" ) ).compare( "on", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
mapUrl.setScheme( "https" );
|
|
}
|
|
else
|
|
{
|
|
mapUrl.setScheme( "http" );
|
|
}
|
|
|
|
QList<QPair<QString, QString> > queryItems = mapUrl.queryItems();
|
|
QList<QPair<QString, QString> >::const_iterator queryIt = queryItems.constBegin();
|
|
for ( ; queryIt != queryItems.constEnd(); ++queryIt )
|
|
{
|
|
if ( queryIt->first.compare( "REQUEST", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
mapUrl.removeQueryItem( queryIt->first );
|
|
}
|
|
else if ( queryIt->first.compare( "VERSION", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
mapUrl.removeQueryItem( queryIt->first );
|
|
}
|
|
else if ( queryIt->first.compare( "SERVICE", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
mapUrl.removeQueryItem( queryIt->first );
|
|
}
|
|
else if ( queryIt->first.compare( "_DC", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
mapUrl.removeQueryItem( queryIt->first );
|
|
}
|
|
}
|
|
hrefString = mapUrl.toString();
|
|
|
|
|
|
// SOAP platform
|
|
//only give this information if it is not a WMS request to be in sync with the WMS capabilities schema
|
|
std::map<QString, QString>::const_iterator service_it = mParameterMap.find( "SERVICE" );
|
|
if ( service_it != mParameterMap.end() && service_it->second.compare( "WMS", Qt::CaseInsensitive ) != 0 )
|
|
{
|
|
QDomElement soapElement = doc.createElement( "SOAP"/*wms:SOAP*/ );
|
|
httpElement.appendChild( soapElement );
|
|
QDomElement soapResourceElement = doc.createElement( "OnlineResource"/*wms:OnlineResource*/ );
|
|
soapResourceElement.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
|
|
soapResourceElement.setAttribute( "xlink:type", "simple" );
|
|
soapResourceElement.setAttribute( "xlink:href", hrefString );
|
|
soapElement.appendChild( soapResourceElement );
|
|
}
|
|
|
|
//only Get supported for the moment
|
|
QDomElement getElement = doc.createElement( "Get"/*wms:Get*/ );
|
|
httpElement.appendChild( getElement );
|
|
QDomElement olResourceElement = doc.createElement( "OnlineResource"/*wms:OnlineResource*/ );
|
|
olResourceElement.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
|
|
olResourceElement.setAttribute( "xlink:type", "simple" );
|
|
requestUrl.truncate( requestUrl.indexOf( "?" ) + 1 );
|
|
olResourceElement.setAttribute( "xlink:href", hrefString );
|
|
getElement.appendChild( olResourceElement );
|
|
|
|
// POST already used by SOAP
|
|
// QDomElement postElement = doc.createElement("post"/*wms:SOAP*/);
|
|
// httpElement.appendChild(postElement);
|
|
// QDomElement postResourceElement = doc.createElement("OnlineResource"/*wms:OnlineResource*/);
|
|
// postResourceElement.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink");
|
|
// postResourceElement.setAttribute("xlink:type","simple");
|
|
// postResourceElement.setAttribute("xlink:href", "http://" + QString(getenv("SERVER_NAME")) + QString(getenv("REQUEST_URI")));
|
|
// postElement.appendChild(postResourceElement);
|
|
// dcpTypeElement.appendChild(postElement);
|
|
|
|
//wms:GetMap
|
|
QDomElement getMapElement = doc.createElement( "GetMap"/*wms:GetMap*/ );
|
|
requestElement.appendChild( getMapElement );
|
|
QDomElement jpgFormatElement = doc.createElement( "Format"/*wms:Format*/ );
|
|
QDomText jpgFormatText = doc.createTextNode( "image/jpeg" );
|
|
jpgFormatElement.appendChild( jpgFormatText );
|
|
getMapElement.appendChild( jpgFormatElement );
|
|
QDomElement pngFormatElement = doc.createElement( "Format"/*wms:Format*/ );
|
|
QDomText pngFormatText = doc.createTextNode( "image/png" );
|
|
pngFormatElement.appendChild( pngFormatText );
|
|
getMapElement.appendChild( pngFormatElement );
|
|
QDomElement getMapDhcTypeElement = dcpTypeElement.cloneNode().toElement();//this is the same as for 'GetCapabilities'
|
|
getMapElement.appendChild( getMapDhcTypeElement );
|
|
|
|
//wms:GetFeatureInfo
|
|
QDomElement getFeatureInfoElem = doc.createElement( "GetFeatureInfo" );
|
|
//text/plain
|
|
QDomElement textFormatElem = doc.createElement( "Format" );
|
|
QDomText textFormatText = doc.createTextNode( "text/plain" );
|
|
textFormatElem.appendChild( textFormatText );
|
|
getFeatureInfoElem.appendChild( textFormatElem );
|
|
//text/html
|
|
QDomElement htmlFormatElem = doc.createElement( "Format" );
|
|
QDomText htmlFormatText = doc.createTextNode( "text/html" );
|
|
htmlFormatElem.appendChild( htmlFormatText );
|
|
getFeatureInfoElem.appendChild( htmlFormatElem );
|
|
//text/xml
|
|
QDomElement xmlFormatElem = doc.createElement( "Format" );
|
|
QDomText xmlFormatText = doc.createTextNode( "text/xml" );
|
|
xmlFormatElem.appendChild( xmlFormatText );
|
|
getFeatureInfoElem.appendChild( xmlFormatElem );
|
|
|
|
//dcpType
|
|
QDomElement getFeatureInfoDhcTypeElement = dcpTypeElement.cloneNode().toElement();//this is the same as for 'GetCapabilities'
|
|
getFeatureInfoElem.appendChild( getFeatureInfoDhcTypeElement );
|
|
requestElement.appendChild( getFeatureInfoElem );
|
|
|
|
//Exception element is mandatory
|
|
QDomElement exceptionElement = doc.createElement( "Exception" );
|
|
QDomElement exFormatElement = doc.createElement( "Format" );
|
|
QDomText formatText = doc.createTextNode( "text/xml" );
|
|
exFormatElement.appendChild( formatText );
|
|
exceptionElement.appendChild( exFormatElement );
|
|
capabilityElement.appendChild( exceptionElement );
|
|
|
|
//Insert <ComposerTemplate> elements derived from wms:_ExtendedCapabilities
|
|
if ( mConfigParser )
|
|
{
|
|
mConfigParser->printCapabilities( capabilityElement, doc );
|
|
}
|
|
|
|
//add the xml content for the individual layers/styles
|
|
QgsDebugMsg( "calling layersAndStylesCapabilities" );
|
|
if ( mConfigParser )
|
|
{
|
|
mConfigParser->layersAndStylesCapabilities( capabilityElement, doc );
|
|
}
|
|
QgsDebugMsg( "layersAndStylesCapabilities returned" );
|
|
|
|
#if 0
|
|
//for debugging: save the document to disk
|
|
QFile capabilitiesFile( QDir::tempPath() + "/capabilities.txt" );
|
|
if ( capabilitiesFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
|
|
{
|
|
QTextStream capabilitiesStream( &capabilitiesFile );
|
|
doc.save( capabilitiesStream, 4 );
|
|
}
|
|
#endif
|
|
return doc;
|
|
}
|
|
|
|
QImage* QgsWMSServer::getLegendGraphics()
|
|
{
|
|
if ( !mConfigParser || !mMapRenderer )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QStringList layersList, stylesList;
|
|
|
|
if ( readLayersAndStyles( layersList, stylesList ) != 0 )
|
|
{
|
|
QgsDebugMsg( "error reading layers and styles" );
|
|
return 0;
|
|
}
|
|
|
|
if ( layersList.size() < 1 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
QgsCoordinateReferenceSystem dummyCRS;
|
|
QStringList layerIds = layerSet( layersList, stylesList, dummyCRS );
|
|
QgsLegendModel legendModel;
|
|
legendModel.setLayerSet( layerIds );
|
|
|
|
//create first image (to find out dpi)
|
|
QImage* theImage = createImage( 10, 10 );
|
|
if ( !theImage )
|
|
{
|
|
return 0;
|
|
}
|
|
double mmToPixelFactor = theImage->dotsPerMeterX() / 1000;
|
|
|
|
//get icon size, spaces between legend items and font from config parser
|
|
double boxSpace, layerSpace, symbolSpace, iconLabelSpace, symbolWidth, symbolHeight;
|
|
boxSpace = mConfigParser->legendBoxSpace() * mmToPixelFactor;
|
|
layerSpace = mConfigParser->legendLayerSpace() * mmToPixelFactor;
|
|
symbolSpace = mConfigParser->legendSymbolSpace() * mmToPixelFactor;
|
|
iconLabelSpace = mConfigParser->legendIconLabelSpace() * mmToPixelFactor;
|
|
symbolWidth = mConfigParser->legendSymbolWidth() * mmToPixelFactor;
|
|
symbolHeight = mConfigParser->legendSymbolHeight() * mmToPixelFactor;
|
|
double maxTextWidth = 0;
|
|
double maxSymbolWidth = 0;
|
|
double currentY = 0;
|
|
double fontOversamplingFactor = 10.0;
|
|
QFont layerFont = mConfigParser->legendLayerFont();
|
|
layerFont.setPixelSize( layerFont.pointSizeF() * 0.3528 * mmToPixelFactor * fontOversamplingFactor );
|
|
QFont itemFont = mConfigParser->legendItemFont();
|
|
itemFont.setPixelSize( itemFont.pointSizeF() * 0.3528 * mmToPixelFactor * fontOversamplingFactor );
|
|
|
|
//first find out image dimensions without painting
|
|
QStandardItem* rootItem = legendModel.invisibleRootItem();
|
|
if ( !rootItem )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int numLayerItems = rootItem->rowCount();
|
|
if ( numLayerItems < 1 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
currentY = boxSpace;
|
|
for ( int i = 0; i < numLayerItems; ++i )
|
|
{
|
|
QgsComposerLayerItem* layerItem = dynamic_cast<QgsComposerLayerItem*>( rootItem->child( i ) );
|
|
if ( layerItem )
|
|
{
|
|
|
|
drawLegendLayerItem( layerItem, 0, maxTextWidth, maxSymbolWidth, currentY, layerFont, itemFont, boxSpace, layerSpace, symbolSpace,
|
|
iconLabelSpace, symbolWidth, symbolHeight, fontOversamplingFactor, theImage->dotsPerMeterX() * 0.0254 );
|
|
}
|
|
}
|
|
currentY += boxSpace;
|
|
|
|
//create second image with the right dimensions
|
|
QImage* paintImage = createImage( maxTextWidth + maxSymbolWidth, currentY );
|
|
|
|
//go through the items a second time for painting
|
|
QPainter p( paintImage );
|
|
p.setRenderHint( QPainter::Antialiasing, true );
|
|
currentY = boxSpace;
|
|
|
|
for ( int i = 0; i < numLayerItems; ++i )
|
|
{
|
|
QgsComposerLayerItem* layerItem = dynamic_cast<QgsComposerLayerItem*>( rootItem->child( i ) );
|
|
if ( layerItem )
|
|
{
|
|
drawLegendLayerItem( layerItem, &p, maxTextWidth, maxSymbolWidth, currentY, layerFont, itemFont, boxSpace, layerSpace, symbolSpace,
|
|
iconLabelSpace, symbolWidth, symbolHeight, fontOversamplingFactor, theImage->dotsPerMeterX() * 0.0254 );
|
|
}
|
|
}
|
|
|
|
QgsMapLayerRegistry::instance()->mapLayers().clear();
|
|
delete theImage;
|
|
return paintImage;
|
|
}
|
|
|
|
QDomDocument QgsWMSServer::getStyle()
|
|
{
|
|
QDomDocument doc;
|
|
std::map<QString, QString>::const_iterator style_it = mParameterMap.find( "STYLE" );
|
|
if ( style_it == mParameterMap.end() )
|
|
{
|
|
throw QgsMapServiceException( "StyleNotSpecified", "Style is mandatory for GetStyle operation" );
|
|
}
|
|
std::map<QString, QString>::const_iterator layer_it = mParameterMap.find( "LAYER" );
|
|
if ( layer_it == mParameterMap.end() )
|
|
{
|
|
throw QgsMapServiceException( "LayerNotSpecified", "Layer is mandatory for GetStyle operation" );
|
|
}
|
|
|
|
QString styleName = style_it->second;
|
|
QString layerName = layer_it->second;
|
|
|
|
return mConfigParser->getStyle( styleName, layerName );
|
|
}
|
|
|
|
// Hack to workaround Qt #5114 by disabling PatternTransform
|
|
class QgsPaintEngineHack : public QPaintEngine
|
|
{
|
|
public:
|
|
void fixFlags()
|
|
{
|
|
gccaps = 0;
|
|
gccaps |= ( QPaintEngine::PrimitiveTransform
|
|
// | QPaintEngine::PatternTransform
|
|
| QPaintEngine::PixmapTransform
|
|
| QPaintEngine::PatternBrush
|
|
// | QPaintEngine::LinearGradientFill
|
|
// | QPaintEngine::RadialGradientFill
|
|
// | QPaintEngine::ConicalGradientFill
|
|
| QPaintEngine::AlphaBlend
|
|
// | QPaintEngine::PorterDuff
|
|
| QPaintEngine::PainterPaths
|
|
| QPaintEngine::Antialiasing
|
|
| QPaintEngine::BrushStroke
|
|
| QPaintEngine::ConstantOpacity
|
|
| QPaintEngine::MaskedBrush
|
|
// | QPaintEngine::PerspectiveTransform
|
|
| QPaintEngine::BlendModes
|
|
// | QPaintEngine::ObjectBoundingModeGradients
|
|
#if QT_VERSION >= 0x040500
|
|
| QPaintEngine::RasterOpModes
|
|
#endif
|
|
| QPaintEngine::PaintOutsidePaintEvent
|
|
);
|
|
}
|
|
};
|
|
|
|
QByteArray* QgsWMSServer::getPrint( const QString& formatString )
|
|
{
|
|
QStringList layersList, stylesList, layerIdList;
|
|
QString dummyFormat;
|
|
QImage* theImage = initializeRendering( layersList, stylesList, layerIdList );
|
|
if ( !theImage )
|
|
{
|
|
return 0;
|
|
}
|
|
delete theImage;
|
|
|
|
QMap<QString, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
|
|
QStringList selectedLayerIdList = applyFeatureSelections( layersList );
|
|
|
|
//GetPrint request needs a template parameter
|
|
std::map<QString, QString>::const_iterator templateIt = mParameterMap.find( "TEMPLATE" );
|
|
if ( templateIt == mParameterMap.end() )
|
|
{
|
|
throw QgsMapServiceException( "ParameterMissing", "The TEMPLATE parameter is required for the GetPrint request" );
|
|
}
|
|
|
|
QgsComposition* c = mConfigParser->createPrintComposition( templateIt->second, mMapRenderer, QMap<QString, QString>( mParameterMap ) );
|
|
if ( !c )
|
|
{
|
|
restoreLayerFilters( originalLayerFilters );
|
|
clearFeatureSelections( selectedLayerIdList );
|
|
return 0;
|
|
}
|
|
|
|
QByteArray* ba = 0;
|
|
c->setPlotStyle( QgsComposition::Print );
|
|
|
|
//SVG export without a running X-Server is a problem. See e.g. http://developer.qt.nokia.com/forums/viewthread/2038
|
|
if ( formatString.compare( "svg", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
c->setPlotStyle( QgsComposition::Print );
|
|
|
|
QSvgGenerator generator;
|
|
ba = new QByteArray();
|
|
QBuffer svgBuffer( ba );
|
|
generator.setOutputDevice( &svgBuffer );
|
|
int width = ( int )( c->paperWidth() * c->printResolution() / 25.4 ); //width in pixel
|
|
int height = ( int )( c->paperHeight() * c->printResolution() / 25.4 ); //height in pixel
|
|
generator.setSize( QSize( width, height ) );
|
|
generator.setResolution( c->printResolution() ); //because the rendering is done in mm, convert the dpi
|
|
|
|
QPainter p( &generator );
|
|
QRectF sourceArea( 0, 0, c->paperWidth(), c->paperHeight() );
|
|
QRectF targetArea( 0, 0, width, height );
|
|
if ( c->printAsRaster() ) //embed one raster into the svg
|
|
{
|
|
QImage* img = printCompositionToImage( c );
|
|
if ( img )
|
|
{
|
|
p.drawImage( targetArea, *img, QRectF( 0, 0, img->width(), img->height() ) );
|
|
}
|
|
delete img;
|
|
}
|
|
else
|
|
{
|
|
c->render( &p, targetArea, sourceArea );
|
|
}
|
|
p.end();
|
|
}
|
|
else if ( formatString.compare( "png", Qt::CaseInsensitive ) == 0 || formatString.compare( "jpg", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
QImage* image = printCompositionToImage( c );
|
|
if ( image )
|
|
{
|
|
ba = new QByteArray();
|
|
QBuffer buffer( ba );
|
|
buffer.open( QIODevice::WriteOnly );
|
|
image->save( &buffer, formatString.toLocal8Bit().data(), -1 );
|
|
}
|
|
delete image;
|
|
}
|
|
else if ( formatString.compare( "pdf", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
QTemporaryFile tempFile;
|
|
if ( !tempFile.open() )
|
|
{
|
|
delete c;
|
|
restoreLayerFilters( originalLayerFilters );
|
|
clearFeatureSelections( selectedLayerIdList );
|
|
return 0;
|
|
}
|
|
|
|
QPrinter printer;
|
|
printer.setResolution( c->printResolution() );
|
|
printer.setFullPage( true );
|
|
printer.setOutputFormat( QPrinter::PdfFormat );
|
|
printer.setOutputFileName( tempFile.fileName() );
|
|
printer.setPaperSize( QSizeF( c->paperWidth(), c->paperHeight() ), QPrinter::Millimeter );
|
|
QRectF paperRectMM = printer.pageRect( QPrinter::Millimeter );
|
|
QRectF paperRectPixel = printer.pageRect( QPrinter::DevicePixel );
|
|
|
|
QPaintEngine *engine = printer.paintEngine();
|
|
if ( engine )
|
|
{
|
|
QgsPaintEngineHack *hack = static_cast<QgsPaintEngineHack*>( engine );
|
|
hack->fixFlags();
|
|
}
|
|
|
|
QPainter p( &printer );
|
|
if ( c->printAsRaster() ) //embed one raster into the pdf
|
|
{
|
|
QImage* img = printCompositionToImage( c );
|
|
if ( img )
|
|
{
|
|
p.drawImage( paperRectPixel, *img, QRectF( 0, 0, img->width(), img->height() ) );
|
|
}
|
|
delete img;
|
|
}
|
|
else //vector pdf
|
|
{
|
|
c->render( &p, paperRectPixel, paperRectMM );
|
|
}
|
|
p.end();
|
|
|
|
ba = new QByteArray();
|
|
*ba = tempFile.readAll();
|
|
}
|
|
else //unknown format
|
|
{
|
|
throw QgsMapServiceException( "InvalidFormat", "Output format '" + formatString + "' is not supported in the GetPrint request" );
|
|
}
|
|
restoreLayerFilters( originalLayerFilters );
|
|
clearFeatureSelections( selectedLayerIdList );
|
|
|
|
delete c;
|
|
return ba;
|
|
}
|
|
|
|
QImage* QgsWMSServer::printCompositionToImage( QgsComposition* c ) const
|
|
{
|
|
int width = ( int )( c->paperWidth() * c->printResolution() / 25.4 ); //width in pixel
|
|
int height = ( int )( c->paperHeight() * c->printResolution() / 25.4 ); //height in pixel
|
|
QImage* image = new QImage( QSize( width, height ), QImage::Format_ARGB32 );
|
|
image->setDotsPerMeterX( c->printResolution() / 25.4 * 1000 );
|
|
image->setDotsPerMeterY( c->printResolution() / 25.4 * 1000 );
|
|
image->fill( 0 );
|
|
QPainter p( image );
|
|
QRectF sourceArea( 0, 0, c->paperWidth(), c->paperHeight() );
|
|
QRectF targetArea( 0, 0, width, height );
|
|
c->render( &p, targetArea, sourceArea );
|
|
p.end();
|
|
return image;
|
|
}
|
|
|
|
QImage* QgsWMSServer::getMap()
|
|
{
|
|
QStringList layersList, stylesList, layerIdList;
|
|
QImage* theImage = initializeRendering( layersList, stylesList, layerIdList );
|
|
|
|
QPainter thePainter( theImage );
|
|
thePainter.setRenderHint( QPainter::Antialiasing ); //make it look nicer
|
|
|
|
QMap<QString, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
|
|
QStringList selectedLayerIdList = applyFeatureSelections( layersList );
|
|
|
|
mMapRenderer->render( &thePainter );
|
|
|
|
restoreLayerFilters( originalLayerFilters );
|
|
clearFeatureSelections( selectedLayerIdList );
|
|
|
|
QgsMapLayerRegistry::instance()->mapLayers().clear();
|
|
return theImage;
|
|
}
|
|
|
|
int QgsWMSServer::getFeatureInfo( QDomDocument& result )
|
|
{
|
|
if ( !mMapRenderer || !mConfigParser )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
result.clear();
|
|
QStringList layersList, stylesList;
|
|
bool conversionSuccess;
|
|
|
|
for ( std::map<QString, QString>::iterator it = mParameterMap.begin(); it != mParameterMap.end(); ++it )
|
|
{
|
|
QgsDebugMsg( QString( "%1 // %2" ).arg( it->first ).arg( it->second ) );
|
|
}
|
|
|
|
if ( readLayersAndStyles( layersList, stylesList ) != 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
if ( initializeSLDParser( layersList, stylesList ) != 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
QImage* outputImage = createImage();
|
|
if ( !outputImage )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if ( configureMapRender( outputImage ) != 0 )
|
|
{
|
|
delete outputImage;
|
|
return 2;
|
|
}
|
|
|
|
//find out the current scale denominater and set it to the SLD parser
|
|
QgsScaleCalculator scaleCalc(( outputImage->logicalDpiX() + outputImage->logicalDpiY() ) / 2 , mMapRenderer->destinationCrs().mapUnits() );
|
|
QgsRectangle mapExtent = mMapRenderer->extent();
|
|
mConfigParser->setScaleDenominator( scaleCalc.calculate( mapExtent, outputImage->width() ) );
|
|
delete outputImage; //no longer needed for feature info
|
|
|
|
//read FEATURE_COUNT
|
|
int featureCount = 1;
|
|
std::map<QString, QString>::const_iterator feature_count_it = mParameterMap.find( "FEATURE_COUNT" );
|
|
if ( feature_count_it != mParameterMap.end() )
|
|
{
|
|
featureCount = feature_count_it->second.toInt( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
{
|
|
featureCount = 1;
|
|
}
|
|
}
|
|
|
|
//read QUERY_LAYERS
|
|
std::map<QString, QString>::const_iterator query_layers_it = mParameterMap.find( "QUERY_LAYERS" );
|
|
if ( query_layers_it == mParameterMap.end() )
|
|
{
|
|
return 3;
|
|
}
|
|
QStringList queryLayerList = query_layers_it->second.split( ",", QString::SkipEmptyParts );
|
|
if ( queryLayerList.size() < 1 )
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
//read I,J resp. X,Y
|
|
QString iString, jString;
|
|
int i = -1;
|
|
int j = -1;
|
|
|
|
std::map<QString, QString>::const_iterator i_it = mParameterMap.find( "I" );
|
|
if ( i_it == mParameterMap.end() )
|
|
{
|
|
std::map<QString, QString>::const_iterator x_it = mParameterMap.find( "X" );
|
|
if ( x_it != mParameterMap.end() )
|
|
{
|
|
iString = x_it->second;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iString = i_it->second;
|
|
}
|
|
i = iString.toInt( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
{
|
|
i = -1;
|
|
}
|
|
|
|
std::map<QString, QString>::const_iterator j_it = mParameterMap.find( "J" );
|
|
if ( j_it == mParameterMap.end() )
|
|
{
|
|
std::map<QString, QString>::const_iterator y_it = mParameterMap.find( "Y" );
|
|
if ( y_it != mParameterMap.end() )
|
|
{
|
|
jString = y_it->second;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jString = j_it->second;
|
|
}
|
|
j = jString.toInt( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
{
|
|
j = -1;
|
|
}
|
|
|
|
//Normally, I/J or X/Y are mandatory parameters.
|
|
//However, in order to make attribute only queries via the FILTER parameter, it is allowed to skip them if the FILTER parameter is there
|
|
|
|
QgsRectangle* featuresRect = 0;
|
|
QgsPoint* infoPoint = 0;
|
|
if ( i == -1 || j == -1 )
|
|
{
|
|
if ( mParameterMap.find( "FILTER" ) != mParameterMap.end() )
|
|
{
|
|
featuresRect = new QgsRectangle();
|
|
}
|
|
else
|
|
{
|
|
throw QgsMapServiceException( "ParameterMissing", "I/J parameters are required for GetFeatureInfo" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
infoPoint = new QgsPoint();
|
|
}
|
|
|
|
//get the layer registered in QgsMapLayerRegistry and apply possible filters
|
|
QStringList layerIds = layerSet( layersList, stylesList, mMapRenderer->destinationCrs() );
|
|
QMap<QString, QString> originalLayerFilters = applyRequestedLayerFilters( layersList );
|
|
|
|
QDomElement getFeatureInfoElement = result.createElement( "GetFeatureInfoResponse" );
|
|
result.appendChild( getFeatureInfoElement );
|
|
|
|
QStringList nonIdentifiableLayers = mConfigParser->identifyDisabledLayers();
|
|
QMap< QString, QMap< int, QString > > aliasInfo = mConfigParser->layerAliasInfo();
|
|
QMap< QString, QSet<QString> > hiddenAttributes = mConfigParser->hiddenAttributes();
|
|
|
|
QList<QgsMapLayer*> layerList;
|
|
QgsMapLayer* currentLayer = 0;
|
|
QStringList::const_iterator layerIt;
|
|
for ( layerIt = queryLayerList.constBegin(); layerIt != queryLayerList.constEnd(); ++layerIt )
|
|
{
|
|
//create maplayers from sld parser (several layers are possible in case of feature info on a group)
|
|
layerList = mConfigParser->mapLayerFromStyle( *layerIt, "" );
|
|
QList<QgsMapLayer*>::iterator layerListIt = layerList.begin();
|
|
for ( ; layerListIt != layerList.end(); ++layerListIt )
|
|
{
|
|
currentLayer = *layerListIt;
|
|
if ( !currentLayer || nonIdentifiableLayers.contains( currentLayer->id() ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( infoPoint && infoPointToLayerCoordinates( i, j, infoPoint, mMapRenderer, currentLayer ) != 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QDomElement layerElement = result.createElement( "Layer" );
|
|
layerElement.setAttribute( "name", currentLayer->name() );
|
|
getFeatureInfoElement.appendChild( layerElement );
|
|
|
|
//switch depending on vector or raster
|
|
QgsVectorLayer* vectorLayer = dynamic_cast<QgsVectorLayer*>( currentLayer );
|
|
if ( vectorLayer )
|
|
{
|
|
//is there alias info for this vector layer?
|
|
QMap< int, QString > layerAliasInfo;
|
|
QMap< QString, QMap< int, QString > >::const_iterator aliasIt = aliasInfo.find( currentLayer->id() );
|
|
if ( aliasIt != aliasInfo.constEnd() )
|
|
{
|
|
layerAliasInfo = aliasIt.value();
|
|
}
|
|
|
|
//hidden attributes for this layer
|
|
QSet<QString> layerHiddenAttributes;
|
|
QMap< QString, QSet<QString> >::const_iterator hiddenIt = hiddenAttributes.find( currentLayer->id() );
|
|
if ( hiddenIt != hiddenAttributes.constEnd() )
|
|
{
|
|
layerHiddenAttributes = hiddenIt.value();
|
|
}
|
|
|
|
if ( featureInfoFromVectorLayer( vectorLayer, infoPoint, featureCount, result, layerElement, mMapRenderer, layerAliasInfo, layerHiddenAttributes, featuresRect ) != 0 )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else //raster layer
|
|
{
|
|
QgsRasterLayer* rasterLayer = dynamic_cast<QgsRasterLayer*>( currentLayer );
|
|
if ( rasterLayer )
|
|
{
|
|
if ( featureInfoFromRasterLayer( rasterLayer, infoPoint, result, layerElement ) != 0 )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( featuresRect )
|
|
{
|
|
QDomElement bBoxElem = result.createElement( "BoundingBox" );
|
|
bBoxElem.setAttribute( "CRS", mMapRenderer->destinationCrs().authid() );
|
|
bBoxElem.setAttribute( "minx", featuresRect->xMinimum() );
|
|
bBoxElem.setAttribute( "maxx", featuresRect->xMaximum() );
|
|
bBoxElem.setAttribute( "miny", featuresRect->yMinimum() );
|
|
bBoxElem.setAttribute( "maxy", featuresRect->yMaximum() );
|
|
getFeatureInfoElement.insertBefore( bBoxElem, QDomNode() ); //insert as first child
|
|
}
|
|
|
|
restoreLayerFilters( originalLayerFilters );
|
|
delete featuresRect;
|
|
delete infoPoint;
|
|
return 0;
|
|
}
|
|
|
|
QImage* QgsWMSServer::initializeRendering( QStringList& layersList, QStringList& stylesList, QStringList& layerIdList )
|
|
{
|
|
if ( !mConfigParser )
|
|
{
|
|
QgsDebugMsg( "Error: mSLDParser is 0" );
|
|
return 0;
|
|
}
|
|
|
|
if ( !mMapRenderer )
|
|
{
|
|
QgsDebugMsg( "Error: mMapRenderer is 0" );
|
|
return 0;
|
|
}
|
|
|
|
if ( readLayersAndStyles( layersList, stylesList ) != 0 )
|
|
{
|
|
QgsDebugMsg( "error reading layers and styles" );
|
|
return 0;
|
|
}
|
|
|
|
if ( initializeSLDParser( layersList, stylesList ) != 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
//pass external GML to the SLD parser.
|
|
std::map<QString, QString>::const_iterator gmlIt = mParameterMap.find( "GML" );
|
|
if ( gmlIt != mParameterMap.end() )
|
|
{
|
|
QDomDocument* gmlDoc = new QDomDocument();
|
|
if ( gmlDoc->setContent( gmlIt->second, true ) )
|
|
{
|
|
QString layerName = gmlDoc->documentElement().attribute( "layerName" );
|
|
QgsDebugMsg( "Adding entry with key: " + layerName + " to external GML data" );
|
|
mConfigParser->addExternalGMLData( layerName, gmlDoc );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "Error, could not add external GML to QgsSLDParser" );
|
|
delete gmlDoc;
|
|
}
|
|
}
|
|
|
|
QImage* theImage = createImage();
|
|
if ( !theImage )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ( configureMapRender( theImage ) != 0 )
|
|
{
|
|
delete theImage;
|
|
return 0;
|
|
}
|
|
mMapRenderer->setLabelingEngine( new QgsPalLabeling() );
|
|
|
|
//find out the current scale denominater and set it to the SLD parser
|
|
QgsScaleCalculator scaleCalc(( theImage->logicalDpiX() + theImage->logicalDpiY() ) / 2 , mMapRenderer->destinationCrs().mapUnits() );
|
|
QgsRectangle mapExtent = mMapRenderer->extent();
|
|
mConfigParser->setScaleDenominator( scaleCalc.calculate( mapExtent, theImage->width() ) );
|
|
|
|
layerIdList = layerSet( layersList, stylesList, mMapRenderer->destinationCrs() );
|
|
#ifdef QGISDEBUG
|
|
QgsDebugMsg( QString( "Number of layers to be rendered. %1" ).arg( layerIdList.count() ) );
|
|
#endif
|
|
mMapRenderer->setLayerSet( layerIdList );
|
|
return theImage;
|
|
}
|
|
|
|
QImage* QgsWMSServer::createImage( int width, int height ) const
|
|
{
|
|
bool conversionSuccess;
|
|
|
|
if ( width < 0 )
|
|
{
|
|
std::map<QString, QString>::const_iterator wit = mParameterMap.find( "WIDTH" );
|
|
if ( wit == mParameterMap.end() )
|
|
{
|
|
width = 0; //width parameter is mandatory
|
|
}
|
|
else
|
|
{
|
|
width = wit->second.toInt( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
{
|
|
width = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( height < 0 )
|
|
{
|
|
std::map<QString, QString>::const_iterator hit = mParameterMap.find( "HEIGHT" );
|
|
if ( hit == mParameterMap.end() )
|
|
{
|
|
height = 0; //height parameter is mandatory
|
|
}
|
|
else
|
|
{
|
|
height = hit->second.toInt( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
{
|
|
height = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( width < 0 || height < 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
QImage* theImage = 0;
|
|
|
|
//is format jpeg?
|
|
bool jpeg = false;
|
|
std::map<QString, QString>::const_iterator formatIt = mParameterMap.find( "FORMAT" );
|
|
if ( formatIt != mParameterMap.end() )
|
|
{
|
|
if ( formatIt->second.compare( "jpg", Qt::CaseInsensitive ) == 0
|
|
|| formatIt->second.compare( "jpeg", Qt::CaseInsensitive ) == 0
|
|
|| formatIt->second.compare( "image/jpeg", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
jpeg = true;
|
|
}
|
|
}
|
|
|
|
//transparent parameter
|
|
bool transparent = false;
|
|
std::map<QString, QString>::const_iterator transparentIt = mParameterMap.find( "TRANSPARENT" );
|
|
if ( transparentIt != mParameterMap.end() && transparentIt->second.compare( "true", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
transparent = true;
|
|
}
|
|
|
|
//use alpha channel only if necessary because it slows down performance
|
|
if ( transparent && !jpeg )
|
|
{
|
|
theImage = new QImage( width, height, QImage::Format_ARGB32_Premultiplied );
|
|
theImage->fill( 0 );
|
|
}
|
|
else
|
|
{
|
|
theImage = new QImage( width, height, QImage::Format_RGB32 );
|
|
theImage->fill( qRgb( 255, 255, 255 ) );
|
|
}
|
|
|
|
if ( !theImage )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//apply DPI parameter if present. This is an extension of QGIS mapserver compared to WMS 1.3.
|
|
//Because of backwards compatibility, this parameter is optional
|
|
std::map<QString, QString>::const_iterator dit = mParameterMap.find( "DPI" );
|
|
if ( dit != mParameterMap.end() )
|
|
{
|
|
int dpi = dit->second.toInt( &conversionSuccess );
|
|
if ( conversionSuccess )
|
|
{
|
|
int dpm = dpi / 0.0254;
|
|
theImage->setDotsPerMeterX( dpm );
|
|
theImage->setDotsPerMeterY( dpm );
|
|
}
|
|
}
|
|
return theImage;
|
|
}
|
|
|
|
int QgsWMSServer::configureMapRender( const QPaintDevice* paintDevice ) const
|
|
{
|
|
if ( !mMapRenderer || !paintDevice )
|
|
{
|
|
return 1; //paint device is needed for height, width, dpi
|
|
}
|
|
|
|
mMapRenderer->setOutputSize( QSize( paintDevice->width(), paintDevice->height() ), paintDevice->logicalDpiX() );
|
|
|
|
//map extent
|
|
bool conversionSuccess;
|
|
double minx, miny, maxx, maxy;
|
|
std::map<QString, QString>::const_iterator bbIt = mParameterMap.find( "BBOX" );
|
|
if ( bbIt == mParameterMap.end() )
|
|
{
|
|
//GetPrint request is allowed to have missing BBOX parameter
|
|
minx = 0; miny = 0; maxx = 0; maxy = 0;
|
|
}
|
|
else
|
|
{
|
|
bool bboxOk = true;
|
|
QString bbString = bbIt->second;
|
|
minx = bbString.section( ",", 0, 0 ).toDouble( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
bboxOk = false;
|
|
miny = bbString.section( ",", 1, 1 ).toDouble( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
bboxOk = false;
|
|
maxx = bbString.section( ",", 2, 2 ).toDouble( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
bboxOk = false;
|
|
maxy = bbString.section( ",", 3, 3 ).toDouble( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
bboxOk = false;
|
|
|
|
if ( !bboxOk )
|
|
{
|
|
//throw a service exception
|
|
throw QgsMapServiceException( "InvalidParameterValue", "Invalid BBOX parameter" );
|
|
}
|
|
}
|
|
|
|
QGis::UnitType mapUnits = QGis::Degrees;
|
|
|
|
std::map<QString, QString>::const_iterator crsIt = mParameterMap.find( "CRS" );
|
|
if ( crsIt == mParameterMap.end() )
|
|
{
|
|
crsIt = mParameterMap.find( "SRS" ); //some clients (e.g. QGIS) call it SRS
|
|
}
|
|
|
|
QgsCoordinateReferenceSystem outputCRS;
|
|
|
|
//wms spec says that CRS parameter is mandatory.
|
|
//we don't rejeict the request if it is not there but disable reprojection on the fly
|
|
if ( crsIt == mParameterMap.end() )
|
|
{
|
|
//disable on the fly projection
|
|
QgsProject::instance()->writeEntry( "SpatialRefSys", "/ProjectionsEnabled", 0 );
|
|
}
|
|
else
|
|
{
|
|
//enable on the fly projection
|
|
QgsDebugMsg( "enable on the fly projection" );
|
|
QgsProject::instance()->writeEntry( "SpatialRefSys", "/ProjectionsEnabled", 1 );
|
|
|
|
QString crsString = crsIt->second;
|
|
if ( !( crsString.left( 4 ) == "EPSG" ) )
|
|
{
|
|
return 3; // only EPSG ids supported
|
|
}
|
|
long epsgId = crsString.section( ":", 1, 1 ).toLong( &conversionSuccess );
|
|
if ( !conversionSuccess )
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
//destination SRS
|
|
outputCRS = QgsEPSGCache::instance()->searchCRS( epsgId );
|
|
if ( !outputCRS.isValid() )
|
|
{
|
|
QgsDebugMsg( "Error, could not create output CRS from EPSG" );
|
|
throw QgsMapServiceException( "InvalidCRS", "Could not create output CRS" );
|
|
return 5;
|
|
}
|
|
mMapRenderer->setDestinationCrs( outputCRS );
|
|
mMapRenderer->setProjectionsEnabled( true );
|
|
mapUnits = outputCRS.mapUnits();
|
|
}
|
|
mMapRenderer->setMapUnits( mapUnits );
|
|
|
|
//Change x- and y- of BBOX for WMS 1.3.0 and geographic coordinate systems
|
|
std::map<QString, QString>::const_iterator versionIt = mParameterMap.find( "VERSION" );
|
|
if ( versionIt != mParameterMap.end() )
|
|
{
|
|
QString version = versionIt->second;
|
|
if ( version == "1.3.0" && outputCRS.geographicFlag() )
|
|
{
|
|
//switch coordinates of extent
|
|
double tmp;
|
|
tmp = minx;
|
|
minx = miny; miny = tmp;
|
|
tmp = maxx;
|
|
maxx = maxy; maxy = tmp;
|
|
}
|
|
}
|
|
|
|
QgsRectangle mapExtent( minx, miny, maxx, maxy );
|
|
mMapRenderer->setExtent( mapExtent );
|
|
|
|
if ( mConfigParser )
|
|
{
|
|
mMapRenderer->setOutputUnits( mConfigParser->outputUnits() );
|
|
}
|
|
else
|
|
{
|
|
mMapRenderer->setOutputUnits( QgsMapRenderer::Pixels ); //SLD units are in pixels normally
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int QgsWMSServer::readLayersAndStyles( QStringList& layersList, QStringList& stylesList ) const
|
|
{
|
|
//get layer and style lists from the parameters
|
|
layersList.clear();
|
|
stylesList.clear();
|
|
std::map<QString, QString>::const_iterator layersIt = mParameterMap.find( "LAYERS" );
|
|
if ( layersIt != mParameterMap.end() )
|
|
{
|
|
layersList = layersIt->second.split( "," );
|
|
}
|
|
|
|
std::map<QString, QString>::const_iterator stylesIt = mParameterMap.find( "STYLES" );
|
|
if ( stylesIt != mParameterMap.end() )
|
|
{
|
|
stylesList = stylesIt->second.split( "," );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int QgsWMSServer::initializeSLDParser( QStringList& layersList, QStringList& stylesList )
|
|
{
|
|
std::map<QString, QString>::const_iterator sldIt = mParameterMap.find( "SLD" );
|
|
if ( sldIt != mParameterMap.end() )
|
|
{
|
|
//ignore LAYERS and STYLES and take those information from the SLD
|
|
QString xml = sldIt->second;
|
|
QDomDocument* theDocument = new QDomDocument( "user.sld" );
|
|
QString errorMsg;
|
|
int errorLine, errorColumn;
|
|
|
|
if ( !theDocument->setContent( xml, true, &errorMsg, &errorLine, &errorColumn ) )
|
|
{
|
|
//std::cout << xml.toAscii().data() << std::endl;
|
|
QgsDebugMsg( "Error, could not create DomDocument from SLD" );
|
|
QgsDebugMsg( QString( "The error message is: %1" ).arg( errorMsg ) );
|
|
delete theDocument;
|
|
return 0;
|
|
}
|
|
QgsSLDParser* userSLDParser = new QgsSLDParser( theDocument );
|
|
userSLDParser->setParameterMap( mParameterMap );
|
|
userSLDParser->setFallbackParser( mConfigParser );
|
|
mConfigParser = userSLDParser;
|
|
//now replace the content of layersList and stylesList (if present)
|
|
layersList.clear();
|
|
stylesList.clear();
|
|
QStringList layersSTDList;
|
|
QStringList stylesSTDList;
|
|
if ( mConfigParser->layersAndStyles( layersSTDList, stylesSTDList ) != 0 )
|
|
{
|
|
QgsDebugMsg( "Error, no layers and styles found in SLD" );
|
|
return 0;
|
|
}
|
|
QStringList::const_iterator layersIt;
|
|
QStringList::const_iterator stylesIt;
|
|
for ( layersIt = layersSTDList.constBegin(), stylesIt = stylesSTDList.constBegin(); layersIt != layersSTDList.constEnd(); ++layersIt, ++stylesIt )
|
|
{
|
|
layersList << *layersIt;
|
|
stylesList << *stylesIt;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int QgsWMSServer::infoPointToLayerCoordinates( int i, int j, QgsPoint* layerCoords, QgsMapRenderer* mapRender,
|
|
QgsMapLayer* layer ) const
|
|
{
|
|
if ( !layerCoords || !mapRender || !layer || !mapRender->coordinateTransform() )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
//first transform i,j to map output coordinates
|
|
QgsPoint mapPoint = mapRender->coordinateTransform()->toMapCoordinates( i, j );
|
|
//and then to layer coordinates
|
|
*layerCoords = mapRender->mapToLayerCoordinates( layer, mapPoint );
|
|
return 0;
|
|
}
|
|
|
|
int QgsWMSServer::featureInfoFromVectorLayer( QgsVectorLayer* layer,
|
|
const QgsPoint* infoPoint,
|
|
int nFeatures,
|
|
QDomDocument& infoDocument,
|
|
QDomElement& layerElement,
|
|
QgsMapRenderer* mapRender,
|
|
QMap<int, QString>& aliasMap,
|
|
QSet<QString>& hiddenAttributes, QgsRectangle* featureBBox ) const
|
|
{
|
|
if ( !layer || !mapRender )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
//we need a selection rect (0.01 of map width)
|
|
QgsRectangle mapRect = mapRender->extent();
|
|
QgsRectangle layerRect = mapRender->mapToLayerCoordinates( layer, mapRect );
|
|
|
|
QgsRectangle searchRect;
|
|
|
|
//info point could be 0 in case there is only an attribute filter
|
|
if ( infoPoint )
|
|
{
|
|
double searchRadius = ( layerRect.xMaximum() - layerRect.xMinimum() ) / 200;
|
|
searchRect.set( infoPoint->x() - searchRadius, infoPoint->y() - searchRadius,
|
|
infoPoint->x() + searchRadius, infoPoint->y() + searchRadius );
|
|
}
|
|
|
|
//do a select with searchRect and go through all the features
|
|
QgsVectorDataProvider* provider = layer->dataProvider();
|
|
if ( !provider )
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
QgsFeature feature;
|
|
QgsAttributeMap featureAttributes;
|
|
int featureCounter = 0;
|
|
const QgsFieldMap& fields = provider->fields();
|
|
bool addWktGeometry = mConfigParser && mConfigParser->featureInfoWithWktGeometry();
|
|
|
|
provider->select( provider->attributeIndexes(), searchRect, addWktGeometry || featureBBox, true );
|
|
while ( provider->nextFeature( feature ) )
|
|
{
|
|
if ( featureCounter > nFeatures )
|
|
{
|
|
break;
|
|
}
|
|
|
|
QDomElement featureElement = infoDocument.createElement( "Feature" );
|
|
featureElement.setAttribute( "id", FID_TO_STRING( feature.id() ) );
|
|
layerElement.appendChild( featureElement );
|
|
|
|
//read all attribute values from the feature
|
|
featureAttributes = feature.attributeMap();
|
|
for ( QgsAttributeMap::const_iterator it = featureAttributes.begin(); it != featureAttributes.end(); ++it )
|
|
{
|
|
|
|
QString attributeName = fields[it.key()].name();
|
|
//skip attribute if it has edit type 'hidden'
|
|
if ( hiddenAttributes.contains( attributeName ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//check if the attribute name should be replaced with an alias
|
|
QMap<int, QString>::const_iterator aliasIt = aliasMap.find( it.key() );
|
|
if ( aliasIt != aliasMap.constEnd() )
|
|
{
|
|
attributeName = aliasIt.value();
|
|
}
|
|
|
|
QDomElement attributeElement = infoDocument.createElement( "Attribute" );
|
|
attributeElement.setAttribute( "name", attributeName );
|
|
attributeElement.setAttribute( "value", it->toString() );
|
|
featureElement.appendChild( attributeElement );
|
|
}
|
|
|
|
//also append the wkt geometry as an attribute
|
|
QgsGeometry* geom = feature.geometry();
|
|
if ( addWktGeometry && geom )
|
|
{
|
|
QDomElement geometryElement = infoDocument.createElement( "Attribute" );
|
|
geometryElement.setAttribute( "name", "geometry" );
|
|
geometryElement.setAttribute( "value", geom->exportToWkt() );
|
|
geometryElement.setAttribute( "type", "derived" );
|
|
featureElement.appendChild( geometryElement );
|
|
}
|
|
if ( featureBBox && geom && mapRender ) //extend feature info bounding box if requested
|
|
{
|
|
QgsRectangle box = mapRender->layerExtentToOutputExtent( layer, geom->boundingBox() );
|
|
if ( featureBBox->isEmpty() )
|
|
{
|
|
*featureBBox = box;
|
|
}
|
|
else
|
|
{
|
|
featureBBox->combineExtentWith( &box );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
int QgsWMSServer::featureInfoFromRasterLayer( QgsRasterLayer* layer,
|
|
const QgsPoint* infoPoint,
|
|
QDomDocument& infoDocument,
|
|
QDomElement& layerElement ) const
|
|
{
|
|
if ( !infoPoint || !layer )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
QMap<QString, QString> attributes;
|
|
layer->identify( *infoPoint, attributes );
|
|
|
|
for ( QMap<QString, QString>::const_iterator it = attributes.constBegin(); it != attributes.constEnd(); ++it )
|
|
{
|
|
QDomElement attributeElement = infoDocument.createElement( "Attribute" );
|
|
attributeElement.setAttribute( "name", it.key() );
|
|
attributeElement.setAttribute( "value", it.value() );
|
|
layerElement.appendChild( attributeElement );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
QStringList QgsWMSServer::layerSet( const QStringList &layersList,
|
|
const QStringList &stylesList,
|
|
const QgsCoordinateReferenceSystem &destCRS ) const
|
|
{
|
|
Q_UNUSED( destCRS );
|
|
QStringList layerKeys;
|
|
QStringList::const_iterator llstIt;
|
|
QStringList::const_iterator slstIt;
|
|
QgsMapLayer* theMapLayer = 0;
|
|
QgsDebugMsg( QString( "Calculating layerset using %1 layers, %2 styles and CRS %3" ).arg( layersList.count() ).arg( stylesList.count() ).arg( destCRS.description() ) );
|
|
for ( llstIt = layersList.begin(), slstIt = stylesList.begin(); llstIt != layersList.end(); ++llstIt )
|
|
{
|
|
|
|
QString styleName;
|
|
if ( slstIt != stylesList.end() )
|
|
{
|
|
styleName = *slstIt;
|
|
}
|
|
QgsDebugMsg( "Trying to get layer " + *llstIt + "//" + styleName );
|
|
|
|
//does the layer name appear several times in the layer list?
|
|
//if yes, layer caching must be disabled because several named layers could have
|
|
//several user styles
|
|
bool allowCaching = true;
|
|
if ( layersList.count( *llstIt ) > 1 )
|
|
{
|
|
allowCaching = false;
|
|
}
|
|
|
|
QList<QgsMapLayer*> layerList = mConfigParser->mapLayerFromStyle( *llstIt, styleName, allowCaching );
|
|
int listIndex;
|
|
|
|
for ( listIndex = layerList.size() - 1; listIndex >= 0; listIndex-- )
|
|
{
|
|
theMapLayer = layerList.at( listIndex );
|
|
QgsDebugMsg( QString( "Checking layer: %1" ).arg( theMapLayer->name() ) );
|
|
if ( theMapLayer )
|
|
{
|
|
layerKeys.push_front( theMapLayer->id() );
|
|
QgsMapLayerRegistry::instance()->addMapLayer( theMapLayer, false );
|
|
}
|
|
else
|
|
{
|
|
QgsDebugMsg( "Layer or style not defined, aborting" );
|
|
throw QgsMapServiceException( "LayerNotDefined", "Layer '" + *llstIt + "' and/or style '" + styleName + "' not defined" );
|
|
}
|
|
}
|
|
|
|
if ( slstIt != stylesList.end() )
|
|
{
|
|
++slstIt;
|
|
}
|
|
}
|
|
return layerKeys;
|
|
}
|
|
|
|
void QgsWMSServer::drawLegendLayerItem( QgsComposerLayerItem* item, QPainter* p, double& maxTextWidth, double& maxSymbolWidth, double& currentY, const QFont& layerFont,
|
|
const QFont& itemFont, double boxSpace, double layerSpace, double symbolSpace,
|
|
double iconLabelSpace, double symbolWidth, double symbolHeight, double fontOversamplingFactor,
|
|
double dpi ) const
|
|
{
|
|
if ( !item )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QFontMetricsF layerFontMetrics( layerFont );
|
|
currentY += layerFontMetrics.ascent() / fontOversamplingFactor;
|
|
|
|
//draw layer title first
|
|
if ( p )
|
|
{
|
|
p->save();
|
|
p->scale( 1.0 / fontOversamplingFactor, 1.0 / fontOversamplingFactor );
|
|
p->setFont( layerFont );
|
|
p->drawText( boxSpace * fontOversamplingFactor, currentY * fontOversamplingFactor, item->text() );
|
|
p->restore();
|
|
}
|
|
else
|
|
{
|
|
double layerItemWidth = layerFontMetrics.width( item->text() ) / fontOversamplingFactor + boxSpace;
|
|
if ( layerItemWidth > maxTextWidth )
|
|
{
|
|
maxTextWidth = layerItemWidth;
|
|
}
|
|
}
|
|
|
|
currentY += layerSpace;
|
|
|
|
int opacity = 0;
|
|
QgsMapLayer* layerInstance = QgsMapLayerRegistry::instance()->mapLayer( item->layerID() );
|
|
if ( layerInstance )
|
|
{
|
|
opacity = layerInstance->getTransparency(); //maplayer says transparency but means opacity
|
|
}
|
|
|
|
//then draw all the children
|
|
if ( p )
|
|
{
|
|
p->setFont( itemFont );
|
|
}
|
|
QFontMetricsF itemFontMetrics( itemFont );
|
|
|
|
int nChildItems = item->rowCount();
|
|
QgsComposerLegendItem* currentComposerItem = 0;
|
|
|
|
for ( int i = 0; i < nChildItems; ++i )
|
|
{
|
|
currentComposerItem = dynamic_cast<QgsComposerLegendItem*>( item->child( i ) );
|
|
if ( !currentComposerItem )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
double currentSymbolHeight = symbolHeight;
|
|
double currentSymbolWidth = symbolWidth; //symbol width (without box space and icon/label space
|
|
double currentTextWidth = 0;
|
|
|
|
//if the font is larger than the standard symbol size, try to draw the symbol centered (shifting towards the bottom)
|
|
double symbolDownShift = ( itemFontMetrics.ascent() / fontOversamplingFactor - symbolHeight ) / 2.0;
|
|
if ( symbolDownShift < 0 )
|
|
{
|
|
symbolDownShift = 0;
|
|
}
|
|
|
|
QgsComposerLegendItem::ItemType type = currentComposerItem->itemType();
|
|
switch ( type )
|
|
{
|
|
case QgsComposerLegendItem::SymbologyItem:
|
|
drawLegendSymbol( currentComposerItem, p, boxSpace, currentY, currentSymbolWidth, currentSymbolHeight, opacity, dpi, symbolDownShift );
|
|
break;
|
|
case QgsComposerLegendItem::SymbologyV2Item:
|
|
drawLegendSymbolV2( currentComposerItem, p, boxSpace, currentY, currentSymbolWidth, currentSymbolHeight, dpi, symbolDownShift );
|
|
break;
|
|
case QgsComposerLegendItem::RasterSymbolItem:
|
|
drawRasterSymbol( currentComposerItem, p, boxSpace, currentY, currentSymbolWidth, currentSymbolHeight, symbolDownShift );
|
|
break;
|
|
case QgsComposerLegendItem::GroupItem:
|
|
//QgsDebugMsg( "GroupItem not handled" );
|
|
break;
|
|
case QgsComposerLegendItem::LayerItem:
|
|
//QgsDebugMsg( "GroupItem not handled" );
|
|
break;
|
|
}
|
|
|
|
//finally draw text
|
|
currentTextWidth = itemFontMetrics.width( currentComposerItem->text() ) / fontOversamplingFactor;
|
|
double symbolItemHeight = qMax( itemFontMetrics.ascent() / fontOversamplingFactor, currentSymbolHeight );
|
|
|
|
if ( p )
|
|
{
|
|
p->save();
|
|
p->scale( 1.0 / fontOversamplingFactor, 1.0 / fontOversamplingFactor );
|
|
p->drawText( maxSymbolWidth * fontOversamplingFactor,
|
|
( currentY + symbolItemHeight / 2.0 ) * fontOversamplingFactor + itemFontMetrics.ascent() / 2.0, currentComposerItem->text() );
|
|
p->restore();
|
|
}
|
|
else
|
|
{
|
|
if ( currentTextWidth > maxTextWidth )
|
|
{
|
|
maxTextWidth = currentTextWidth;
|
|
}
|
|
double symbolWidth = boxSpace + currentSymbolWidth + iconLabelSpace;
|
|
if ( symbolWidth > maxSymbolWidth )
|
|
{
|
|
maxSymbolWidth = symbolWidth;
|
|
}
|
|
}
|
|
|
|
currentY += symbolItemHeight;
|
|
currentY += symbolSpace;
|
|
}
|
|
}
|
|
|
|
void QgsWMSServer::drawLegendSymbol( QgsComposerLegendItem* item, QPainter* p, double boxSpace, double currentY, double& symbolWidth, double& symbolHeight, double layerOpacity,
|
|
double dpi, double yDownShift ) const
|
|
{
|
|
QgsComposerSymbolItem* symbolItem = dynamic_cast< QgsComposerSymbolItem* >( item );
|
|
if ( !symbolItem )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QgsSymbol* symbol = symbolItem->symbol();
|
|
if ( !symbol )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QGis::GeometryType symbolType = symbol->type();
|
|
switch ( symbolType )
|
|
{
|
|
case QGis::Point:
|
|
drawPointSymbol( p, symbol, boxSpace, currentY, symbolWidth, symbolHeight, layerOpacity, dpi );
|
|
break;
|
|
case QGis::Line:
|
|
drawLineSymbol( p, symbol, boxSpace, currentY, symbolWidth, symbolHeight, layerOpacity, yDownShift );
|
|
break;
|
|
case QGis::Polygon:
|
|
drawPolygonSymbol( p, symbol, boxSpace, currentY, symbolWidth, symbolHeight, layerOpacity, yDownShift );
|
|
break;
|
|
case QGis::UnknownGeometry:
|
|
case QGis::NoGeometry:
|
|
// shouldn't occur
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QgsWMSServer::drawPointSymbol( QPainter* p, QgsSymbol* s, double boxSpace, double currentY, double& symbolWidth, double& symbolHeight, double layerOpacity, double dpi ) const
|
|
{
|
|
if ( !s )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QImage pointImage;
|
|
|
|
//width scale is 1.0
|
|
pointImage = s->getPointSymbolAsImage( dpi / 25.4, false, Qt::yellow, 1.0, 0.0, 1.0, layerOpacity / 255.0 );
|
|
|
|
if ( p )
|
|
{
|
|
QPointF imageTopLeft( boxSpace, currentY );
|
|
p->drawImage( imageTopLeft, pointImage );
|
|
}
|
|
|
|
symbolWidth = pointImage.width();
|
|
symbolHeight = pointImage.height();
|
|
}
|
|
|
|
void QgsWMSServer::drawPolygonSymbol( QPainter* p, QgsSymbol* s, double boxSpace, double currentY, double symbolWidth, double symbolHeight, double layerOpacity, double yDownShift ) const
|
|
{
|
|
if ( !s || !p )
|
|
{
|
|
return;
|
|
}
|
|
|
|
p->save();
|
|
|
|
//brush
|
|
QBrush symbolBrush = s->brush();
|
|
QColor brushColor = symbolBrush.color();
|
|
brushColor.setAlpha( layerOpacity );
|
|
symbolBrush.setColor( brushColor );
|
|
p->setBrush( symbolBrush );
|
|
|
|
//pen
|
|
QPen symbolPen = s->pen();
|
|
QColor penColor = symbolPen.color();
|
|
penColor.setAlpha( layerOpacity );
|
|
symbolPen.setColor( penColor );
|
|
p->setPen( symbolPen );
|
|
|
|
p->drawRect( QRectF( boxSpace, currentY + yDownShift, symbolWidth, symbolHeight ) );
|
|
|
|
p->restore();
|
|
}
|
|
|
|
void QgsWMSServer::drawLineSymbol( QPainter* p, QgsSymbol* s, double boxSpace, double currentY, double symbolWidth, double symbolHeight, double layerOpacity, double yDownShift ) const
|
|
{
|
|
if ( !s || !p )
|
|
{
|
|
return;
|
|
}
|
|
|
|
p->save();
|
|
QPen symbolPen = s->pen();
|
|
QColor penColor = symbolPen.color();
|
|
penColor.setAlpha( layerOpacity );
|
|
symbolPen.setColor( penColor );
|
|
symbolPen.setCapStyle( Qt::FlatCap );
|
|
p->setPen( symbolPen );
|
|
double lineY = currentY + symbolHeight / 2.0 + symbolPen.widthF() / 2.0 + yDownShift;
|
|
p->drawLine( QPointF( boxSpace, lineY ), QPointF( boxSpace + symbolWidth, lineY ) );
|
|
p->restore();
|
|
}
|
|
|
|
void QgsWMSServer::drawLegendSymbolV2( QgsComposerLegendItem* item, QPainter* p, double boxSpace, double currentY, double& symbolWidth,
|
|
double& symbolHeight, double dpi, double yDownShift ) const
|
|
{
|
|
QgsComposerSymbolV2Item* symbolItem = dynamic_cast< QgsComposerSymbolV2Item* >( item );
|
|
if ( !symbolItem )
|
|
{
|
|
return;
|
|
}
|
|
QgsSymbolV2* symbol = symbolItem->symbolV2();
|
|
if ( !symbol )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//markers might have a different size
|
|
QgsMarkerSymbolV2* markerSymbol = dynamic_cast< QgsMarkerSymbolV2* >( symbol );
|
|
if ( markerSymbol )
|
|
{
|
|
symbolWidth = markerSymbol->size() * dpi / 25.4;
|
|
symbolHeight = markerSymbol->size() * dpi / 25.4;
|
|
}
|
|
|
|
double rasterScaleFactor = dpi / 2.0 / 25.4;
|
|
|
|
if ( p )
|
|
{
|
|
p->save();
|
|
p->translate( boxSpace, currentY + yDownShift );
|
|
p->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
|
|
symbol->drawPreviewIcon( p, QSize( symbolWidth * rasterScaleFactor, symbolHeight * rasterScaleFactor ) );
|
|
p->restore();
|
|
}
|
|
}
|
|
|
|
void QgsWMSServer::drawRasterSymbol( QgsComposerLegendItem* item, QPainter* p, double boxSpace, double currentY, double symbolWidth, double symbolHeight, double yDownShift ) const
|
|
{
|
|
if ( !item || ! p )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QgsComposerRasterSymbolItem* rasterItem = dynamic_cast< QgsComposerRasterSymbolItem* >( item );
|
|
if ( !rasterItem )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QgsRasterLayer* layer = qobject_cast< QgsRasterLayer* >( QgsMapLayerRegistry::instance()->mapLayer( rasterItem->layerID() ) );
|
|
if ( !layer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
p->setBrush( QBrush( rasterItem->color() ) );
|
|
p->drawRect( QRectF( boxSpace, currentY + yDownShift, symbolWidth, symbolHeight ) );
|
|
}
|
|
|
|
QMap<QString, QString> QgsWMSServer::applyRequestedLayerFilters( const QStringList& layerList ) const
|
|
{
|
|
QMap<QString, QString> filterMap;
|
|
|
|
if ( layerList.isEmpty() )
|
|
{
|
|
return filterMap;
|
|
}
|
|
|
|
std::map<QString, QString>::const_iterator filterIt = mParameterMap.find( "FILTER" );
|
|
if ( filterIt != mParameterMap.end() )
|
|
{
|
|
QString filterParameter = filterIt->second;
|
|
QStringList layerSplit = filterParameter.split( ";" );
|
|
QStringList::const_iterator layerIt = layerSplit.constBegin();
|
|
for ( ; layerIt != layerSplit.constEnd(); ++layerIt )
|
|
{
|
|
QStringList eqSplit = layerIt->split( ":" );
|
|
if ( eqSplit.size() < 2 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//filter string could be unsafe (danger of sql injection)
|
|
if ( !testFilterStringSafety( eqSplit.at( 1 ) ) )
|
|
{
|
|
throw QgsMapServiceException( "Filter string rejected", "The filter string " + eqSplit.at( 1 ) +
|
|
" has been rejected because of security reasons. Note: Text strings have to be enclosed in single or double quotes. " +
|
|
"A space between each word / special character is mandatory. Allowed Keywords and special characters are " +
|
|
"AND,OR,IN,<,>=,>,>=,!=,',',(,). Not allowed are semicolons in the filter expression." );
|
|
}
|
|
|
|
//we need to find the maplayer objects matching the layer name
|
|
QList<QgsMapLayer*> layersToFilter;
|
|
QMap<QString, QgsMapLayer*>& layerMap = QgsMapLayerRegistry::instance()->mapLayers();
|
|
|
|
QMap<QString, QgsMapLayer*>::iterator layerIt = layerMap.begin();
|
|
for ( ; layerIt != layerMap.end(); ++layerIt )
|
|
{
|
|
if ( layerIt.value() && layerIt.value()->name() == eqSplit.at( 0 ) )
|
|
{
|
|
layersToFilter.push_back( layerIt.value() );
|
|
}
|
|
}
|
|
|
|
QList<QgsMapLayer*>::iterator filterIt = layersToFilter.begin();
|
|
for ( ; filterIt != layersToFilter.end(); ++filterIt )
|
|
{
|
|
QgsVectorLayer* filteredLayer = dynamic_cast<QgsVectorLayer*>( *filterIt );
|
|
if ( filteredLayer )
|
|
{
|
|
filterMap.insert( filteredLayer->id(), filteredLayer->subsetString() );
|
|
QString newSubsetString = eqSplit.at( 1 );
|
|
if ( !filteredLayer->subsetString().isEmpty() )
|
|
{
|
|
newSubsetString.prepend( " AND " );
|
|
newSubsetString.prepend( filteredLayer->subsetString() );
|
|
}
|
|
filteredLayer->setSubsetString( newSubsetString );
|
|
}
|
|
}
|
|
}
|
|
|
|
//No BBOX parameter in request. We use the union of the filtered layer
|
|
//to provide the functionality of zooming to selected records via (enhanced) WMS.
|
|
if ( mMapRenderer && mMapRenderer->extent().isEmpty() )
|
|
{
|
|
QgsRectangle filterExtent;
|
|
QMap<QString, QString>::const_iterator filterIt = filterMap.constBegin();
|
|
for ( ; filterIt != filterMap.constEnd(); ++filterIt )
|
|
{
|
|
QgsMapLayer* mapLayer = QgsMapLayerRegistry::instance()->mapLayer( filterIt.key() );
|
|
if ( !mapLayer )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QgsRectangle layerExtent = mapLayer->extent();
|
|
if ( filterExtent.isEmpty() )
|
|
{
|
|
filterExtent = layerExtent;
|
|
}
|
|
else
|
|
{
|
|
filterExtent.combineExtentWith( &layerExtent );
|
|
}
|
|
}
|
|
mMapRenderer->setExtent( filterExtent );
|
|
}
|
|
|
|
//No BBOX parameter in request. We use the union of the filtered layer
|
|
//to provide the functionality of zooming to selected records via (enhanced) WMS.
|
|
if ( mMapRenderer && mMapRenderer->extent().isEmpty() )
|
|
{
|
|
QgsRectangle filterExtent;
|
|
QMap<QString, QString>::const_iterator filterIt = filterMap.constBegin();
|
|
for ( ; filterIt != filterMap.constEnd(); ++filterIt )
|
|
{
|
|
QgsMapLayer* mapLayer = QgsMapLayerRegistry::instance()->mapLayer( filterIt.key() );
|
|
if ( !mapLayer )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QgsRectangle layerExtent = mapLayer->extent();
|
|
if ( filterExtent.isEmpty() )
|
|
{
|
|
filterExtent = layerExtent;
|
|
}
|
|
else
|
|
{
|
|
filterExtent.combineExtentWith( &layerExtent );
|
|
}
|
|
}
|
|
mMapRenderer->setExtent( filterExtent );
|
|
}
|
|
}
|
|
return filterMap;
|
|
}
|
|
|
|
void QgsWMSServer::restoreLayerFilters( const QMap < QString, QString >& filterMap ) const
|
|
{
|
|
QMap < QString, QString >::const_iterator filterIt = filterMap.constBegin();
|
|
for ( ; filterIt != filterMap.constEnd(); ++filterIt )
|
|
{
|
|
QgsVectorLayer* filteredLayer = dynamic_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( filterIt.key() ) );
|
|
if ( filteredLayer )
|
|
{
|
|
QgsVectorDataProvider* dp = filteredLayer->dataProvider();
|
|
if ( dp )
|
|
{
|
|
dp->setSubsetString( filterIt.value() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool QgsWMSServer::testFilterStringSafety( const QString& filter ) const
|
|
{
|
|
//; too dangerous for sql injections
|
|
if ( filter.contains( ";" ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
QStringList tokens = filter.split( " ", QString::SkipEmptyParts );
|
|
QStringList::const_iterator tokenIt = tokens.constBegin();
|
|
for ( ; tokenIt != tokens.constEnd(); ++tokenIt )
|
|
{
|
|
//whitelist of allowed characters and keywords
|
|
if ( tokenIt->compare( "," ) == 0
|
|
|| tokenIt->compare( "(" ) == 0
|
|
|| tokenIt->compare( ")" ) == 0
|
|
|| tokenIt->compare( "=" ) == 0
|
|
|| tokenIt->compare( "!=" ) == 0
|
|
|| tokenIt->compare( "<" ) == 0
|
|
|| tokenIt->compare( "<=" ) == 0
|
|
|| tokenIt->compare( ">" ) == 0
|
|
|| tokenIt->compare( ">=" ) == 0
|
|
|| tokenIt->compare( "AND", Qt::CaseInsensitive ) == 0
|
|
|| tokenIt->compare( "OR", Qt::CaseInsensitive ) == 0
|
|
|| tokenIt->compare( "IN", Qt::CaseInsensitive ) == 0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//numbers are ok
|
|
bool isNumeric;
|
|
tokenIt->toDouble( &isNumeric );
|
|
if ( isNumeric )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//numeric strings need to be quoted once either with single or with double quotes
|
|
|
|
//single quote
|
|
if ( tokenIt->size() > 2
|
|
&& ( *tokenIt )[0] == QChar( '\'' )
|
|
&& ( *tokenIt )[tokenIt->size() - 1] == QChar( '\'' )
|
|
&& ( *tokenIt )[1] != QChar( '\'' )
|
|
&& ( *tokenIt )[tokenIt->size() - 2] != QChar( '\'' ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//double quote
|
|
if ( tokenIt->size() > 2
|
|
&& ( *tokenIt )[0] == QChar( '"' )
|
|
&& ( *tokenIt )[tokenIt->size() - 1] == QChar( '"' )
|
|
&& ( *tokenIt )[1] != QChar( '"' )
|
|
&& ( *tokenIt )[tokenIt->size() - 2] != QChar( '"' ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
QStringList QgsWMSServer::applyFeatureSelections( const QStringList& layerList ) const
|
|
{
|
|
QStringList layersWithSelections;
|
|
if ( layerList.isEmpty() )
|
|
{
|
|
return layersWithSelections;
|
|
}
|
|
|
|
std::map<QString, QString>::const_iterator selectionIt = mParameterMap.find( "SELECTION" );
|
|
if ( selectionIt == mParameterMap.end() )
|
|
{
|
|
return layersWithSelections;
|
|
}
|
|
|
|
QString selectionString = selectionIt->second;
|
|
QStringList selectionLayerList = selectionString.split( ";" );
|
|
QStringList::const_iterator selectionLayerIt = selectionLayerList.constBegin();
|
|
for ( ; selectionLayerIt != selectionLayerList.constEnd(); ++selectionLayerIt )
|
|
{
|
|
//separate layer name from id list
|
|
QStringList layerIdSplit = selectionLayerIt->split( ":" );
|
|
if ( layerIdSplit.size() < 2 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//find layerId for layer name
|
|
QString layerName = layerIdSplit.at( 0 );
|
|
QgsVectorLayer* vLayer = 0;
|
|
|
|
QMap<QString, QgsMapLayer*>& layerMap = QgsMapLayerRegistry::instance()->mapLayers();
|
|
QMap<QString, QgsMapLayer*>::iterator layerIt = layerMap.begin();
|
|
for ( ; layerIt != layerMap.end(); ++layerIt )
|
|
{
|
|
if ( layerIt.value() && layerIt.value()->name() == layerName )
|
|
{
|
|
vLayer = qobject_cast<QgsVectorLayer*>( layerIt.value() );
|
|
layersWithSelections.push_back( vLayer->id() );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !vLayer )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
QStringList idList = layerIdSplit.at( 1 ).split( "," );
|
|
QgsFeatureIds selectedIds;
|
|
|
|
QStringList::const_iterator idIt = idList.constBegin();
|
|
for ( ; idIt != idList.constEnd(); ++idIt )
|
|
{
|
|
selectedIds.insert( STRING_TO_FID( *idIt ) );
|
|
}
|
|
|
|
vLayer->setSelectedFeatures( selectedIds );
|
|
}
|
|
|
|
|
|
return layersWithSelections;
|
|
}
|
|
|
|
void QgsWMSServer::clearFeatureSelections( const QStringList& layerIds ) const
|
|
{
|
|
QMap<QString, QgsMapLayer*>& layerMap = QgsMapLayerRegistry::instance()->mapLayers();
|
|
|
|
QStringList::const_iterator layerIdIt = layerIds.constBegin();
|
|
for ( ; layerIdIt != layerIds.constEnd(); ++layerIdIt )
|
|
{
|
|
QMap<QString, QgsMapLayer*>::iterator layerIt = layerMap.find( *layerIdIt );
|
|
if ( layerIt != layerMap.end() )
|
|
{
|
|
QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( layerIt.value() );
|
|
if ( vlayer )
|
|
{
|
|
vlayer->setSelectedFeatures( QgsFeatureIds() );
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|