Skip to content

Commit 806afc9

Browse files
author
mhugent
committedDec 24, 2010
[FEATURE]: initial support for wms printing with GetPrint-Request
git-svn-id: http://svn.osgeo.org/qgis/trunk@14973 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 9cb73c0 commit 806afc9

15 files changed

+567
-82
lines changed
 

‎src/mapserver/qgis_map_serv.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,28 @@ int main( int argc, char * argv[] )
382382
continue;
383383

384384
}
385+
else if ( requestIt->second == "GetPrint" )
386+
{
387+
QByteArray* printOutput = 0;
388+
QString formatString;
389+
try
390+
{
391+
printOutput = theServer->getPrint( formatString );
392+
}
393+
catch ( QgsMapServiceException& ex )
394+
{
395+
theRequestHandler->sendServiceException( ex );
396+
}
397+
398+
if ( printOutput )
399+
{
400+
theRequestHandler->sendGetPrintResponse( printOutput, formatString );
401+
}
402+
delete printOutput;
403+
delete theRequestHandler;
404+
delete theServer;
405+
continue;
406+
}
385407
else//unknown request
386408
{
387409
QgsMapServiceException e( "OperationNotSupported", "Operation " + requestIt->second + " not supported" );

‎src/mapserver/qgsconfigparser.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include "qgsconfigparser.h"
1919
#include "qgsapplication.h"
20+
#include "qgscomposermap.h"
21+
#include "qgscomposition.h"
2022
#include "qgsrasterlayer.h"
2123
#include "qgsvectorlayer.h"
2224
#include <sqlite3.h>
@@ -269,3 +271,108 @@ void QgsConfigParser::appendCRSElementsToLayer( QDomElement& layerElement, QDomD
269271
layerElement.appendChild( crsElement );
270272
}
271273
}
274+
275+
QgsComposition* QgsConfigParser::createPrintComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, const QMap< QString, QString >& parameterMap ) const
276+
{
277+
QList<QgsComposerMap*> composerMaps;
278+
QList<QgsComposerLabel*> composerLabels;
279+
280+
QgsComposition* c = initComposition( composerTemplate, mapRenderer, composerMaps, composerLabels );
281+
if ( !c )
282+
{
283+
return 0;
284+
}
285+
286+
QMap< QString, QString >::const_iterator dpiIt = parameterMap.find( "DPI" );
287+
if ( dpiIt != parameterMap.constEnd() )
288+
{
289+
c->setPrintResolution( dpiIt.value().toInt() );
290+
}
291+
292+
//replace composer map parameters
293+
QList<QgsComposerMap*>::iterator mapIt = composerMaps.begin();
294+
QgsComposerMap* currentMap = 0;
295+
for ( ; mapIt != composerMaps.end(); ++mapIt )
296+
{
297+
currentMap = *mapIt;
298+
if ( !currentMap )
299+
{
300+
continue;
301+
}
302+
303+
//search composer map title in parameter map-> string
304+
QMap< QString, QString >::const_iterator titleIt = parameterMap.find( "MAP" + QString::number( currentMap->id() ) );
305+
if ( titleIt == parameterMap.constEnd() )
306+
{
307+
continue;
308+
}
309+
QString replaceString = titleIt.value();
310+
QStringList replacementList = replaceString.split( "/" );
311+
312+
//get map extent from string
313+
if ( replacementList.size() > 0 )
314+
{
315+
QStringList coordList = replacementList.at( 0 ).split( "," );
316+
if ( coordList.size() > 3 )
317+
{
318+
bool xMinOk, yMinOk, xMaxOk, yMaxOk;
319+
double xmin = coordList.at( 0 ).toDouble( &xMinOk );
320+
double ymin = coordList.at( 1 ).toDouble( &yMinOk );
321+
double xmax = coordList.at( 2 ).toDouble( &xMaxOk );
322+
double ymax = coordList.at( 3 ).toDouble( &yMaxOk );
323+
if ( xMinOk && yMinOk && xMaxOk && yMaxOk )
324+
{
325+
currentMap->setNewExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
326+
}
327+
}
328+
}
329+
330+
//get rotation from string
331+
if ( replacementList.size() > 1 )
332+
{
333+
bool rotationOk;
334+
double rotation = replacementList.at( 1 ).toDouble( &rotationOk );
335+
if ( rotationOk )
336+
{
337+
currentMap->setMapRotation( rotation );
338+
}
339+
}
340+
341+
//get layer list from string
342+
if ( replacementList.size() > 2 )
343+
{
344+
QStringList layerSet;
345+
QStringList wmsLayerList = replacementList.at( 2 ).split( "," );
346+
QStringList wmsStyleList;
347+
if ( replacementList.size() > 3 )
348+
{
349+
wmsStyleList = replacementList.at( 3 ).split( "," );
350+
}
351+
352+
for ( int i = 0; i < wmsLayerList.size(); ++i )
353+
{
354+
QString styleName;
355+
if ( wmsStyleList.size() > i )
356+
{
357+
styleName = wmsStyleList.at( i );
358+
}
359+
QList<QgsMapLayer*> layerList = mapLayerFromStyle( wmsLayerList.at( i ), styleName );
360+
QList<QgsMapLayer*>::const_iterator mapIdIt = layerList.constBegin();
361+
for ( ; mapIdIt != layerList.constEnd(); ++mapIdIt )
362+
{
363+
if ( *mapIdIt )
364+
{
365+
layerSet.push_back(( *mapIdIt )->getLayerID() );
366+
}
367+
}
368+
}
369+
370+
currentMap->setLayerSet( layerSet );
371+
currentMap->setKeepLayerSet( true );
372+
}
373+
}
374+
375+
//replace composer label text
376+
377+
return c; //soon...
378+
}

‎src/mapserver/qgsconfigparser.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
#include <QList>
2525
#include <QSet>
2626

27+
class QgsComposition;
28+
class QgsComposerLabel;
29+
class QgsComposerMap;
2730
class QDomElement;
2831

2932
/**Interface class for configuration parsing, e.g. SLD configuration or QGIS project file*/
@@ -56,6 +59,7 @@ class QgsConfigParser
5659

5760
/**Sets fallback parser (does not take ownership)*/
5861
void setFallbackParser( QgsConfigParser* p );
62+
const QgsConfigParser* fallbackParser() { return mFallbackParser; }
5963

6064
void setScaleDenominator( double denom ) {mScaleDenominator = denom;}
6165

@@ -88,6 +92,15 @@ class QgsConfigParser
8892
/**Returns information about vector attributes with hidden edit type. Key is layer id, value is a set containing the names of the hidden attributes*/
8993
virtual QMap< QString, QSet<QString> > hiddenAttributes() const { return QMap< QString, QSet<QString> >(); }
9094

95+
/**Creates a print composition, usually for a GetPrint request. Replaces map and label parameters*/
96+
QgsComposition* createPrintComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, const QMap< QString, QString >& parameterMap ) const;
97+
98+
/**Creates a composition from the project file (probably delegated to the fallback parser)*/
99+
virtual QgsComposition* initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const = 0;
100+
101+
/**Adds print capabilities to xml document. ParentElem usually is the <Capabilities> element*/
102+
virtual void printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const = 0;
103+
91104
protected:
92105
/**Parser to forward not resolved requests (e.g. SLD parser based on user request might have a fallback parser with admin configuration)*/
93106
QgsConfigParser* mFallbackParser;

‎src/mapserver/qgsgetrequesthandler.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,6 @@ std::map<QString, QString> QgsGetRequestHandler::parseInput()
107107
{
108108
formatString = "PNG";
109109
}
110-
else
111-
{
112-
throw QgsMapServiceException( "InvalidFormat", "Invalid format, only jpg and png are supported" );
113-
}
114110
mFormat = formatString;
115111
}
116112
}
@@ -301,3 +297,8 @@ void QgsGetRequestHandler::sendServiceException( const QgsMapServiceException& e
301297
QByteArray ba = exceptionDoc.toByteArray();
302298
sendHttpResponse( &ba, "text/xml" );
303299
}
300+
301+
void QgsGetRequestHandler::sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const
302+
{
303+
sendHttpResponse( ba, formatString );
304+
}

‎src/mapserver/qgsgetrequesthandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,5 @@ class QgsGetRequestHandler: public QgsHttpRequestHandler
2929
void sendGetFeatureInfoResponse( const QDomDocument& infoDoc, const QString& infoFormat ) const;
3030
void sendServiceException( const QgsMapServiceException& ex ) const;
3131
void sendGetStyleResponse( const QDomDocument& doc ) const;
32+
void sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const;
3233
};

‎src/mapserver/qgsmslayercache.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ QgsMapLayer* QgsMSLayerCache::searchLayer( const QString& url, const QString& la
7979
QMap<QPair<QString, QString>, QgsMSLayerCacheEntry>::iterator it = mEntries.find( urlNamePair );
8080
if ( it == mEntries.end() )
8181
{
82+
QgsMSDebugMsg( "Layer not found in cache" )
8283
return 0;
8384
}
8485
else
@@ -92,6 +93,7 @@ QgsMapLayer* QgsMSLayerCache::searchLayer( const QString& url, const QString& la
9293
vl->removeOverlay( "diagram" );
9394
}
9495
#endif //DIAGRAMSERVER
96+
QgsMSDebugMsg( "Layer found in cache" )
9597
return it->layerPointer;
9698
}
9799
}

‎src/mapserver/qgsprojectparser.cpp

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@
2222
#include "qgsrasterlayer.h"
2323
#include "qgsvectorlayer.h"
2424

25+
#include "qgscomposition.h"
26+
#include "qgscomposerarrow.h"
27+
#include "qgscomposerattributetable.h"
28+
#include "qgscomposerlabel.h"
29+
#include "qgscomposerlegend.h"
30+
#include "qgscomposermap.h"
31+
#include "qgscomposerpicture.h"
32+
#include "qgscomposerscalebar.h"
33+
#include "qgscomposershape.h"
34+
2535
QgsProjectParser::QgsProjectParser( QDomDocument* xmlDoc ): QgsConfigParser(), mXMLDoc( xmlDoc )
2636
{
2737
mOutputUnits = QgsMapRenderer::Millimeters;
@@ -814,3 +824,161 @@ QString QgsProjectParser::layerIdFromLegendLayer( const QDomElement& legendLayer
814824
return legendLayerFileList.at( 0 ).toElement().attribute( "layerid" );
815825
}
816826

827+
QgsComposition* QgsProjectParser::initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const
828+
{
829+
//Create composition from xml
830+
QDomElement composerElem = composerByName( composerTemplate );
831+
if ( composerElem.isNull() )
832+
{
833+
return 0;
834+
}
835+
836+
QDomElement compositionElem = composerElem.firstChildElement( "Composition" );
837+
if ( compositionElem.isNull() )
838+
{
839+
return 0;
840+
}
841+
842+
QgsComposition* composition = new QgsComposition( mapRenderer ); //set resolution, paper size from composer element attributes
843+
if ( !composition->readXML( compositionElem, *mXMLDoc ) )
844+
{
845+
delete composition;
846+
return 0;
847+
}
848+
849+
QList<QDomElement> scaleBarElemList;
850+
851+
//go through all the item elements and add them to the composition (and to the lists)
852+
QDomNodeList itemNodes = composerElem.childNodes();
853+
for ( int i = 0; i < itemNodes.size(); ++i )
854+
{
855+
QDomElement currentElem = itemNodes.at( i ).toElement();
856+
QString elemName = currentElem.tagName();
857+
if ( elemName == "ComposerMap" )
858+
{
859+
QgsComposerMap* map = new QgsComposerMap( composition );
860+
map->readXML( currentElem, *mXMLDoc );
861+
composition->addItem( map );
862+
mapList.push_back( map );
863+
}
864+
else if ( elemName == "ComposerLabel" )
865+
{
866+
QgsComposerLabel* label = new QgsComposerLabel( composition );
867+
label->readXML( currentElem, *mXMLDoc );
868+
composition->addItem( label );
869+
labelList.push_back( label );
870+
}
871+
else if ( elemName == "ComposerLegend" )
872+
{
873+
QgsComposerLegend* legend = new QgsComposerLegend( composition );
874+
legend->readXML( currentElem, *mXMLDoc );
875+
composition->addItem( legend );
876+
}
877+
else if ( elemName == "ComposerPicture" )
878+
{
879+
QgsComposerPicture* picture = new QgsComposerPicture( composition );
880+
picture->readXML( currentElem, *mXMLDoc );
881+
composition->addItem( picture );
882+
}
883+
else if ( elemName == "ComposerScaleBar" )
884+
{
885+
//scalebars need to be loaded after the composer maps
886+
scaleBarElemList.push_back( currentElem );
887+
}
888+
else if ( elemName == "ComposerShape" )
889+
{
890+
QgsComposerShape* shape = new QgsComposerShape( composition );
891+
shape->readXML( currentElem, *mXMLDoc );
892+
composition->addItem( shape );
893+
}
894+
else if ( elemName == "ComposerArrow" )
895+
{
896+
QgsComposerArrow* arrow = new QgsComposerArrow( composition );
897+
arrow->readXML( currentElem, *mXMLDoc );
898+
composition->addItem( arrow );
899+
}
900+
else if ( elemName == "ComposerAttributeTable" )
901+
{
902+
QgsComposerAttributeTable* table = new QgsComposerAttributeTable( composition );
903+
table->readXML( currentElem, *mXMLDoc );
904+
composition->addItem( table );
905+
}
906+
}
907+
908+
//scalebars need to be loaded after the composer maps to receive the correct size
909+
QList<QDomElement>::const_iterator scaleBarIt = scaleBarElemList.constBegin();
910+
for ( ; scaleBarIt != scaleBarElemList.constEnd(); ++scaleBarIt )
911+
{
912+
QgsComposerScaleBar* bar = new QgsComposerScaleBar( composition );
913+
bar->readXML( *scaleBarIt, *mXMLDoc );
914+
composition->addItem( bar );
915+
}
916+
917+
return composition;
918+
}
919+
920+
void QgsProjectParser::printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const
921+
{
922+
if ( !mXMLDoc )
923+
{
924+
return;
925+
}
926+
927+
QDomNodeList composerNodeList = mXMLDoc->elementsByTagName( "Composer" );
928+
for ( int i = 0; i < composerNodeList.size(); ++i )
929+
{
930+
QDomElement composerTemplateElem = doc.createElement( "ComposerTemplate" );
931+
QDomElement currentComposerElem = composerNodeList.at( i ).toElement();
932+
if ( currentComposerElem.isNull() )
933+
{
934+
continue;
935+
}
936+
937+
composerTemplateElem.setAttribute( "name", currentComposerElem.attribute( "title" ) );
938+
composerTemplateElem.setAttribute( "xmlns:wms", "http://www.opengis.net/wms" );
939+
composerTemplateElem.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
940+
composerTemplateElem.setAttribute( "xsi:type", "wms:_ExtendedCapabilities" );
941+
942+
//add available composer maps and their size in mm
943+
QDomNodeList composerMapList = currentComposerElem.elementsByTagName( "ComposerMap" );
944+
for ( int j = 0; j < composerMapList.size(); ++j )
945+
{
946+
QDomElement cmap = composerMapList.at( j ).toElement();
947+
QDomElement citem = cmap.firstChildElement( "ComposerItem" );
948+
if ( citem.isNull() )
949+
{
950+
continue;
951+
}
952+
953+
QDomElement composerMapElem = doc.createElement( "ComposerMap" );
954+
composerMapElem.setAttribute( "name", "map" + cmap.attribute( "id" ) );
955+
composerMapElem.setAttribute( "width", citem.attribute( "width" ) );
956+
composerMapElem.setAttribute( "height", citem.attribute( "height" ) );
957+
composerTemplateElem.appendChild( composerMapElem );
958+
}
959+
960+
parentElement.appendChild( composerTemplateElem );
961+
}
962+
}
963+
964+
QDomElement QgsProjectParser::composerByName( const QString& composerName ) const
965+
{
966+
QDomElement composerElem;
967+
if ( !mXMLDoc )
968+
{
969+
return composerElem;
970+
}
971+
972+
QDomNodeList composerNodeList = mXMLDoc->elementsByTagName( "Composer" );
973+
for ( int i = 0; i < composerNodeList.size(); ++i )
974+
{
975+
QDomElement currentComposerElem = composerNodeList.at( i ).toElement();
976+
if ( currentComposerElem.attribute( "title" ) == composerName )
977+
{
978+
return currentComposerElem;
979+
}
980+
}
981+
982+
return composerElem;
983+
}
984+

‎src/mapserver/qgsprojectparser.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ class QgsProjectParser: public QgsConfigParser
8585
/**Return project title*/
8686
QString projectTitle() const;
8787

88+
const QDomDocument* xmlDoc() const { return mXMLDoc; }
89+
90+
/**Creates a composition from the project file (probably delegated to the fallback parser)*/
91+
QgsComposition* initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const;
92+
93+
/**Adds print capabilities to xml document. ParentElem usually is the <Capabilities> element*/
94+
void printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
95+
8896
private:
8997
/**Content of project file*/
9098
QDomDocument* mXMLDoc;
@@ -118,6 +126,9 @@ class QgsProjectParser: public QgsConfigParser
118126
const QStringList &nonIdentifiableLayers,
119127
const QgsRectangle &mapExtent,
120128
const QgsCoordinateReferenceSystem &mapCRS ) const;
129+
130+
/**Returns dom element of composer (identified by composer title) or a null element in case of error*/
131+
QDomElement composerByName( const QString& composerName ) const;
121132
};
122133

123134
#endif // QGSPROJECTPARSER_H

‎src/mapserver/qgsrequesthandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class QgsRequestHandler
4040
virtual void sendGetFeatureInfoResponse( const QDomDocument& infoDoc, const QString& infoFormat ) const = 0;
4141
virtual void sendServiceException( const QgsMapServiceException& ex ) const = 0;
4242
virtual void sendGetStyleResponse( const QDomDocument& doc ) const = 0;
43+
virtual void sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const = 0;
4344
protected:
4445
/**This is set by the parseInput methods of the subclasses (parameter FORMAT, e.g. 'FORMAT=PNG')*/
4546
QString mFormat;

‎src/mapserver/qgssldparser.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,23 @@ void QgsSLDParser::setCrsForLayer( const QDomElement& layerElem, QgsMapLayer* ml
15391539
}
15401540
}
15411541

1542+
QgsComposition* QgsSLDParser::initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const
1543+
{
1544+
if ( mFallbackParser )
1545+
{
1546+
return mFallbackParser->initComposition( composerTemplate, mapRenderer, mapList, labelList );
1547+
}
1548+
return 0;
1549+
}
1550+
1551+
void QgsSLDParser::printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const
1552+
{
1553+
if ( mFallbackParser )
1554+
{
1555+
mFallbackParser->printCapabilities( parentElement, doc );
1556+
}
1557+
}
1558+
15421559
#ifdef DIAGRAMSERVER
15431560
int QgsSLDParser::overlaysFromUserStyle( const QDomElement& userStyleElement, QgsVectorLayer* vec ) const
15441561
{

‎src/mapserver/qgssldparser.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ class QgsSLDParser: public QgsConfigParser
6868

6969
virtual void setParameterMap( const std::map<QString, QString>& parameterMap ) { mParameterMap = parameterMap; }
7070

71+
/**Creates a composition from the project file (delegated to the fallback parser)*/
72+
QgsComposition* initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const;
73+
74+
/**Adds print capabilities to xml document. Delegated to fallback parser*/
75+
void printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
76+
7177
private:
7278
/**Don't use the default constructor*/
7379
QgsSLDParser();

‎src/mapserver/qgssoaprequesthandler.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,10 @@ void QgsSOAPRequestHandler::sendGetStyleResponse( const QDomDocument& infoDoc )
469469
sendHttpResponse( &ba, "text/xml" );
470470
}
471471

472-
472+
void QgsSOAPRequestHandler::sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const
473+
{
474+
//soon...
475+
}
473476

474477
void QgsSOAPRequestHandler::sendServiceException( const QgsMapServiceException& ex ) const
475478
{

‎src/mapserver/qgssoaprequesthandler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class QgsSOAPRequestHandler: public QgsHttpRequestHandler
3434
void sendGetFeatureInfoResponse( const QDomDocument& infoDoc, const QString& infoFormat ) const;
3535
void sendServiceException( const QgsMapServiceException& ex ) const;
3636
void sendGetStyleResponse( const QDomDocument& doc ) const;
37+
void sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const;
3738
private:
3839
/**Parses the xml of a getMap request and fills the parameters into the map. Returns 0 in case of success*/
3940
int parseGetMapElement( std::map<QString, QString>& parameterMap, const QDomElement& getMapElement ) const;

‎src/mapserver/qgswmsserver.cpp

Lines changed: 195 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@
4545
#include <QTextStream>
4646
#include <QDir>
4747

48+
//for printing
49+
#include "qgscomposition.h"
50+
#include <QBuffer>
51+
#include <QPrinter>
52+
#include <QSvgGenerator>
53+
4854
QgsWMSServer::QgsWMSServer( std::map<QString, QString> parameters, QgsMapRenderer* renderer )
4955
: mParameterMap( parameters )
5056
, mConfigParser( 0 )
@@ -208,18 +214,27 @@ QDomDocument QgsWMSServer::getCapabilities()
208214
exceptionElement.appendChild( exFormatElement );
209215
capabilityElement.appendChild( exceptionElement );
210216

217+
//Insert <ComposerTemplate> elements derived from wms:_ExtendedCapabilities
218+
if ( mConfigParser )
219+
{
220+
mConfigParser->printCapabilities( capabilityElement, doc );
221+
}
222+
211223
//add the xml content for the individual layers/styles
212224
QgsMSDebugMsg( "calling layersAndStylesCapabilities" )
213-
mConfigParser->layersAndStylesCapabilities( capabilityElement, doc );
225+
if ( mConfigParser )
226+
{
227+
mConfigParser->layersAndStylesCapabilities( capabilityElement, doc );
228+
}
214229
QgsMSDebugMsg( "layersAndStylesCapabilities returned" )
215230

216231
//for debugging: save the document to disk
217-
QFile capabilitiesFile( QDir::tempPath() + "/capabilities.txt" );
232+
/*QFile capabilitiesFile( QDir::tempPath() + "/capabilities.txt" );
218233
if ( capabilitiesFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
219234
{
220235
QTextStream capabilitiesStream( &capabilitiesFile );
221236
doc.save( capabilitiesStream, 4 );
222-
}
237+
}*/
223238
return doc;
224239
}
225240

@@ -340,81 +355,104 @@ QDomDocument QgsWMSServer::getStyle()
340355
return mConfigParser->getStyle( styleName, layerName );
341356
}
342357

343-
QImage* QgsWMSServer::getMap()
358+
QByteArray* QgsWMSServer::getPrint( QString& formatString )
344359
{
345-
QgsMSDebugMsg( "Entering" )
346-
if ( !mConfigParser )
347-
{
348-
QgsMSDebugMsg( "Error: mSLDParser is 0" )
349-
return 0;
350-
}
351-
352-
if ( !mMapRenderer )
360+
QStringList layersList, stylesList, layerIdList;
361+
QImage* theImage = initializeRendering( layersList, stylesList, layerIdList, formatString );
362+
if ( !theImage )
353363
{
354-
QgsMSDebugMsg( "Error: mMapRenderer is 0" )
355364
return 0;
356365
}
366+
delete theImage;
357367

358-
QStringList layersList, stylesList;
359-
QString formatString;
360-
361-
if ( readLayersAndStyles( layersList, stylesList ) != 0 )
368+
//GetPrint request needs a template parameter
369+
std::map<QString, QString>::const_iterator templateIt = mParameterMap.find( "TEMPLATE" );
370+
if ( templateIt == mParameterMap.end() )
362371
{
363-
QgsMSDebugMsg( "error reading layers and styles" )
364372
return 0;
365373
}
366374

367-
if ( initializeSLDParser( layersList, stylesList ) != 0 )
375+
QgsComposition* c = mConfigParser->createPrintComposition( templateIt->second, mMapRenderer, QMap<QString, QString>( mParameterMap ) );
376+
if ( !c )
368377
{
369378
return 0;
370379
}
371380

372-
//pass external GML to the SLD parser.
373-
std::map<QString, QString>::const_iterator gmlIt = mParameterMap.find( "GML" );
374-
if ( gmlIt != mParameterMap.end() )
375-
{
376-
QDomDocument* gmlDoc = new QDomDocument();
377-
if ( gmlDoc->setContent( gmlIt->second, true ) )
378-
{
379-
QString layerName = gmlDoc->documentElement().attribute( "layerName" );
380-
QgsMSDebugMsg( "Adding entry with key: " + layerName + " to external GML data" )
381-
mConfigParser->addExternalGMLData( layerName, gmlDoc );
382-
}
383-
else
381+
QByteArray* ba = 0;
382+
c->setPlotStyle( QgsComposition::Print );
383+
384+
//SVG export without a running X-Server is a problem. See e.g. http://developer.qt.nokia.com/forums/viewthread/2038
385+
if ( formatString.compare( "svg", Qt::CaseInsensitive ) == 0 )
386+
{
387+
c->setPlotStyle( QgsComposition::Print );
388+
389+
QSvgGenerator generator;
390+
ba = new QByteArray();
391+
QBuffer svgBuffer( ba );
392+
generator.setOutputDevice( &svgBuffer );
393+
int width = ( int )( c->paperWidth() * c->printResolution() / 25.4 ); //width in pixel
394+
int height = ( int )( c->paperHeight() * c->printResolution() / 25.4 ); //height in pixel
395+
generator.setSize( QSize( width, height ) );
396+
generator.setResolution( c->printResolution() ); //because the rendering is done in mm, convert the dpi
397+
398+
QPainter p( &generator );
399+
QRectF sourceArea( 0, 0, c->paperWidth(), c->paperHeight() );
400+
QRectF targetArea( 0, 0, width, height );
401+
c->render( &p, targetArea, sourceArea );
402+
p.end();
403+
}
404+
else if ( formatString.compare( "png", Qt::CaseInsensitive ) == 0 )
405+
{
406+
int width = ( int )( c->paperWidth() * c->printResolution() / 25.4 ); //width in pixel
407+
int height = ( int )( c->paperHeight() * c->printResolution() / 25.4 ); //height in pixel
408+
QImage image( QSize( width, height ), QImage::Format_ARGB32 );
409+
image.setDotsPerMeterX( c->printResolution() / 25.4 * 1000 );
410+
image.setDotsPerMeterY( c->printResolution() / 25.4 * 1000 );
411+
image.fill( 0 );
412+
QPainter p( &image );
413+
QRectF sourceArea( 0, 0, c->paperWidth(), c->paperHeight() );
414+
QRectF targetArea( 0, 0, width, height );
415+
c->render( &p, targetArea, sourceArea );
416+
p.end();
417+
ba = new QByteArray();
418+
QBuffer buffer( ba );
419+
buffer.open( QIODevice::WriteOnly );
420+
image.save( &buffer, "png", -1 );
421+
}
422+
else if ( formatString.compare( "pdf", Qt::CaseInsensitive ) == 0 )
423+
{
424+
QTemporaryFile tempFile;
425+
if ( !tempFile.open() )
384426
{
385-
QgsMSDebugMsg( "Error, could not add external GML to QgsSLDParser" )
386-
delete gmlDoc;
427+
delete c;
428+
return 0;
387429
}
388-
}
389430

390-
//get output format
391-
std::map<QString, QString>::const_iterator outIt = mParameterMap.find( "FORMAT" );
392-
if ( outIt == mParameterMap.end() )
393-
{
394-
QgsMSDebugMsg( "Error, no parameter FORMAT found" )
395-
return 0; //output format parameter also mandatory
396-
}
431+
QPrinter printer;
432+
printer.setOutputFormat( QPrinter::PdfFormat );
433+
printer.setOutputFileName( tempFile.fileName() );
434+
printer.setPaperSize( QSizeF( c->paperWidth(), c->paperHeight() ), QPrinter::Millimeter );
435+
printer.setResolution( c->printResolution() );
397436

398-
QImage* theImage = createImage();
399-
if ( !theImage )
400-
{
401-
return 0;
402-
}
437+
QRectF paperRectMM = printer.pageRect( QPrinter::Millimeter );
438+
QRectF paperRectPixel = printer.pageRect( QPrinter::DevicePixel );
439+
QPainter p( &printer );
440+
c->render( &p, paperRectPixel, paperRectMM );
441+
p.end();
403442

404-
if ( configureMapRender( theImage ) != 0 )
405-
{
406-
return 0;
443+
ba = new QByteArray();
444+
*ba = tempFile.readAll();
407445
}
408-
mMapRenderer->setLabelingEngine( new QgsPalLabeling() );
409446

410-
//find out the current scale denominater and set it to the SLD parser
411-
QgsScaleCalculator scaleCalc(( theImage->logicalDpiX() + theImage->logicalDpiY() ) / 2 , mMapRenderer->destinationSrs().mapUnits() );
412-
QgsRectangle mapExtent = mMapRenderer->extent();
413-
mConfigParser->setScaleDenominator( scaleCalc.calculate( mapExtent, theImage->width() ) );
447+
delete c;
448+
return ba;
449+
}
414450

415-
//create objects for qgis rendering
416-
QStringList theLayers = layerSet( layersList, stylesList, mMapRenderer->destinationSrs() );
417-
mMapRenderer->setLayerSet( theLayers );
451+
QImage* QgsWMSServer::getMap()
452+
{
453+
QStringList layersList, stylesList, layerIdList;
454+
QString outputFormat;
455+
QImage* theImage = initializeRendering( layersList, stylesList, layerIdList, outputFormat );
418456

419457
QPainter thePainter( theImage );
420458
thePainter.setRenderHint( QPainter::Antialiasing ); //make it look nicer
@@ -610,6 +648,82 @@ int QgsWMSServer::getFeatureInfo( QDomDocument& result )
610648
return 0;
611649
}
612650

651+
QImage* QgsWMSServer::initializeRendering( QStringList& layersList, QStringList& stylesList, QStringList& layerIdList, QString& outputFormat )
652+
{
653+
if ( !mConfigParser )
654+
{
655+
QgsMSDebugMsg( "Error: mSLDParser is 0" )
656+
return 0;
657+
}
658+
659+
if ( !mMapRenderer )
660+
{
661+
QgsMSDebugMsg( "Error: mMapRenderer is 0" )
662+
return 0;
663+
}
664+
665+
if ( readLayersAndStyles( layersList, stylesList ) != 0 )
666+
{
667+
QgsMSDebugMsg( "error reading layers and styles" )
668+
return 0;
669+
}
670+
671+
if ( initializeSLDParser( layersList, stylesList ) != 0 )
672+
{
673+
return 0;
674+
}
675+
676+
//pass external GML to the SLD parser.
677+
std::map<QString, QString>::const_iterator gmlIt = mParameterMap.find( "GML" );
678+
if ( gmlIt != mParameterMap.end() )
679+
{
680+
QDomDocument* gmlDoc = new QDomDocument();
681+
if ( gmlDoc->setContent( gmlIt->second, true ) )
682+
{
683+
QString layerName = gmlDoc->documentElement().attribute( "layerName" );
684+
QgsMSDebugMsg( "Adding entry with key: " + layerName + " to external GML data" )
685+
mConfigParser->addExternalGMLData( layerName, gmlDoc );
686+
}
687+
else
688+
{
689+
QgsMSDebugMsg( "Error, could not add external GML to QgsSLDParser" )
690+
delete gmlDoc;
691+
}
692+
}
693+
694+
//get output format
695+
std::map<QString, QString>::const_iterator outIt = mParameterMap.find( "FORMAT" );
696+
if ( outIt == mParameterMap.end() )
697+
{
698+
QgsMSDebugMsg( "Error, no parameter FORMAT found" )
699+
return 0; //output format parameter also mandatory
700+
}
701+
outputFormat = outIt->second;
702+
703+
QImage* theImage = createImage();
704+
if ( !theImage )
705+
{
706+
return 0;
707+
}
708+
709+
if ( configureMapRender( theImage ) != 0 )
710+
{
711+
delete theImage;
712+
return 0;
713+
}
714+
mMapRenderer->setLabelingEngine( new QgsPalLabeling() );
715+
716+
//find out the current scale denominater and set it to the SLD parser
717+
QgsScaleCalculator scaleCalc(( theImage->logicalDpiX() + theImage->logicalDpiY() ) / 2 , mMapRenderer->destinationSrs().mapUnits() );
718+
QgsRectangle mapExtent = mMapRenderer->extent();
719+
mConfigParser->setScaleDenominator( scaleCalc.calculate( mapExtent, theImage->width() ) );
720+
721+
//create objects for qgis rendering
722+
QStringList theLayers = layerSet( layersList, stylesList, mMapRenderer->destinationSrs() );
723+
mMapRenderer->setLayerSet( theLayers );
724+
return theImage;
725+
}
726+
613727
QImage* QgsWMSServer::createImage( int width, int height ) const
614728
{
615729
bool conversionSuccess;
@@ -619,12 +733,12 @@ QImage* QgsWMSServer::createImage( int width, int height ) const
619733
std::map<QString, QString>::const_iterator wit = mParameterMap.find( "WIDTH" );
620734
if ( wit == mParameterMap.end() )
621735
{
622-
return 0; //width parameter is mandatory
736+
width = 0; //width parameter is mandatory
623737
}
624738
width = wit->second.toInt( &conversionSuccess );
625739
if ( !conversionSuccess )
626740
{
627-
return 0;
741+
width = 0;
628742
}
629743
}
630744

@@ -633,12 +747,12 @@ QImage* QgsWMSServer::createImage( int width, int height ) const
633747
std::map<QString, QString>::const_iterator hit = mParameterMap.find( "HEIGHT" );
634748
if ( hit == mParameterMap.end() )
635749
{
636-
return 0; //height parameter is mandatory
750+
height = 0; //height parameter is mandatory
637751
}
638752
height = hit->second.toInt( &conversionSuccess );
639753
if ( !conversionSuccess )
640754
{
641-
return 0;
755+
height = 0;
642756
}
643757
}
644758

@@ -718,23 +832,27 @@ int QgsWMSServer::configureMapRender( const QPaintDevice* paintDevice ) const
718832
std::map<QString, QString>::const_iterator bbIt = mParameterMap.find( "BBOX" );
719833
if ( bbIt == mParameterMap.end() )
720834
{
721-
return 2;
835+
//GetPrint request is allowed to have missing BBOX parameter
836+
minx = 0; miny = 0; maxx = 0; maxy = 0;
722837
}
723-
bool bboxOk = true;
724-
QString bbString = bbIt->second;
725-
minx = bbString.section( ",", 0, 0 ).toDouble( &conversionSuccess );
726-
if ( !conversionSuccess ) {bboxOk = false;}
727-
miny = bbString.section( ",", 1, 1 ).toDouble( &conversionSuccess );
728-
if ( !conversionSuccess ) {bboxOk = false;}
729-
maxx = bbString.section( ",", 2, 2 ).toDouble( &conversionSuccess );
730-
if ( !conversionSuccess ) {bboxOk = false;}
731-
maxy = bbString.section( ",", 3, 3 ).toDouble( &conversionSuccess );
732-
if ( !conversionSuccess ) {bboxOk = false;}
733-
734-
if ( !bboxOk )
735-
{
736-
//throw a service exception
737-
throw QgsMapServiceException( "InvalidParameterValue", "Invalid BBOX parameter" );
838+
else
839+
{
840+
bool bboxOk = true;
841+
QString bbString = bbIt->second;
842+
minx = bbString.section( ",", 0, 0 ).toDouble( &conversionSuccess );
843+
if ( !conversionSuccess ) {bboxOk = false;}
844+
miny = bbString.section( ",", 1, 1 ).toDouble( &conversionSuccess );
845+
if ( !conversionSuccess ) {bboxOk = false;}
846+
maxx = bbString.section( ",", 2, 2 ).toDouble( &conversionSuccess );
847+
if ( !conversionSuccess ) {bboxOk = false;}
848+
maxy = bbString.section( ",", 3, 3 ).toDouble( &conversionSuccess );
849+
if ( !conversionSuccess ) {bboxOk = false;}
850+
851+
if ( !bboxOk )
852+
{
853+
//throw a service exception
854+
throw QgsMapServiceException( "InvalidParameterValue", "Invalid BBOX parameter" );
855+
}
738856
}
739857

740858
QGis::UnitType mapUnits = QGis::Degrees;

‎src/mapserver/qgswmsserver.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ class QgsWMSServer
6060
/**Returns an SLD file with the style of the requested layer. Exception is raised in case of troubles :-)*/
6161
QDomDocument getStyle();
6262

63+
/**Returns printed page as binary
64+
@param formatString out: format of the print output (e.g. pdf, svg, png, ...)
65+
@return printed page as binary or 0 in case of error*/
66+
QByteArray* getPrint( QString& formatString );
67+
6368
/**Creates an xml document that describes the result of the getFeatureInfo request.
6469
@return 0 in case of success*/
6570
int getFeatureInfo( QDomDocument& result );
@@ -70,6 +75,15 @@ class QgsWMSServer
7075
private:
7176
/**Don't use the default constructor*/
7277
QgsWMSServer();
78+
79+
/**Initializes WMS layers and configures mMapRendering.
80+
@param layersList out: list with WMS layer names
81+
@param stylesList out: list with WMS style names
82+
@param layerIdList out: list with QGIS layer ids
83+
@param outputFormat out: name of requested output format
84+
@return image configured together with mMapRenderer (or 0 in case of error). The calling function takes ownership of the image*/
85+
QImage* initializeRendering( QStringList& layersList, QStringList& stylesList, QStringList& layerIdList, QString& outputFormat );
86+
7387
/**Creates a QImage from the HEIGHT and WIDTH parameters
7488
@param width image width (or -1 if width should be taken from WIDTH wms parameter)
7589
@param height image height (or -1 if height should be taken from HEIGHT wms parameter)

0 commit comments

Comments
 (0)
Please sign in to comment.