[QGIS Commit] r14973 - trunk/qgis/src/mapserver

svn_qgis at osgeo.org svn_qgis at osgeo.org
Fri Dec 24 09:11:13 EST 2010


Author: mhugent
Date: 2010-12-24 06:11:13 -0800 (Fri, 24 Dec 2010)
New Revision: 14973

Modified:
   trunk/qgis/src/mapserver/qgis_map_serv.cpp
   trunk/qgis/src/mapserver/qgsconfigparser.cpp
   trunk/qgis/src/mapserver/qgsconfigparser.h
   trunk/qgis/src/mapserver/qgsgetrequesthandler.cpp
   trunk/qgis/src/mapserver/qgsgetrequesthandler.h
   trunk/qgis/src/mapserver/qgsmslayercache.cpp
   trunk/qgis/src/mapserver/qgsprojectparser.cpp
   trunk/qgis/src/mapserver/qgsprojectparser.h
   trunk/qgis/src/mapserver/qgsrequesthandler.h
   trunk/qgis/src/mapserver/qgssldparser.cpp
   trunk/qgis/src/mapserver/qgssldparser.h
   trunk/qgis/src/mapserver/qgssoaprequesthandler.cpp
   trunk/qgis/src/mapserver/qgssoaprequesthandler.h
   trunk/qgis/src/mapserver/qgswmsserver.cpp
   trunk/qgis/src/mapserver/qgswmsserver.h
Log:
[FEATURE]: initial support for wms printing with GetPrint-Request

Modified: trunk/qgis/src/mapserver/qgis_map_serv.cpp
===================================================================
--- trunk/qgis/src/mapserver/qgis_map_serv.cpp	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgis_map_serv.cpp	2010-12-24 14:11:13 UTC (rev 14973)
@@ -382,6 +382,28 @@
       continue;
 
     }
+    else if ( requestIt->second == "GetPrint" )
+    {
+      QByteArray* printOutput = 0;
+      QString formatString;
+      try
+      {
+        printOutput = theServer->getPrint( formatString );
+      }
+      catch ( QgsMapServiceException& ex )
+      {
+        theRequestHandler->sendServiceException( ex );
+      }
+
+      if ( printOutput )
+      {
+        theRequestHandler->sendGetPrintResponse( printOutput, formatString );
+      }
+      delete printOutput;
+      delete theRequestHandler;
+      delete theServer;
+      continue;
+    }
     else//unknown request
     {
       QgsMapServiceException e( "OperationNotSupported", "Operation " + requestIt->second + " not supported" );

Modified: trunk/qgis/src/mapserver/qgsconfigparser.cpp
===================================================================
--- trunk/qgis/src/mapserver/qgsconfigparser.cpp	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgsconfigparser.cpp	2010-12-24 14:11:13 UTC (rev 14973)
@@ -17,6 +17,8 @@
 
 #include "qgsconfigparser.h"
 #include "qgsapplication.h"
+#include "qgscomposermap.h"
+#include "qgscomposition.h"
 #include "qgsrasterlayer.h"
 #include "qgsvectorlayer.h"
 #include <sqlite3.h>
@@ -269,3 +271,108 @@
     layerElement.appendChild( crsElement );
   }
 }
+
+QgsComposition* QgsConfigParser::createPrintComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, const QMap< QString, QString >& parameterMap ) const
+{
+  QList<QgsComposerMap*> composerMaps;
+  QList<QgsComposerLabel*> composerLabels;
+
+  QgsComposition* c = initComposition( composerTemplate, mapRenderer, composerMaps, composerLabels );
+  if ( !c )
+  {
+    return 0;
+  }
+
+  QMap< QString, QString >::const_iterator dpiIt = parameterMap.find( "DPI" );
+  if ( dpiIt != parameterMap.constEnd() )
+  {
+    c->setPrintResolution( dpiIt.value().toInt() );
+  }
+
+  //replace composer map parameters
+  QList<QgsComposerMap*>::iterator mapIt = composerMaps.begin();
+  QgsComposerMap* currentMap = 0;
+  for ( ; mapIt != composerMaps.end(); ++mapIt )
+  {
+    currentMap = *mapIt;
+    if ( !currentMap )
+    {
+      continue;
+    }
+
+    //search composer map title in parameter map-> string
+    QMap< QString, QString >::const_iterator titleIt = parameterMap.find( "MAP" + QString::number( currentMap->id() ) );
+    if ( titleIt == parameterMap.constEnd() )
+    {
+      continue;
+    }
+    QString replaceString = titleIt.value();
+    QStringList replacementList = replaceString.split( "/" );
+
+    //get map extent from string
+    if ( replacementList.size() > 0 )
+    {
+      QStringList coordList = replacementList.at( 0 ).split( "," );
+      if ( coordList.size() > 3 )
+      {
+        bool xMinOk, yMinOk, xMaxOk, yMaxOk;
+        double xmin = coordList.at( 0 ).toDouble( &xMinOk );
+        double ymin = coordList.at( 1 ).toDouble( &yMinOk );
+        double xmax = coordList.at( 2 ).toDouble( &xMaxOk );
+        double ymax = coordList.at( 3 ).toDouble( &yMaxOk );
+        if ( xMinOk && yMinOk && xMaxOk && yMaxOk )
+        {
+          currentMap->setNewExtent( QgsRectangle( xmin, ymin, xmax, ymax ) );
+        }
+      }
+    }
+
+    //get rotation from string
+    if ( replacementList.size() > 1 )
+    {
+      bool rotationOk;
+      double rotation = replacementList.at( 1 ).toDouble( &rotationOk );
+      if ( rotationOk )
+      {
+        currentMap->setMapRotation( rotation );
+      }
+    }
+
+    //get layer list from string
+    if ( replacementList.size() > 2 )
+    {
+      QStringList layerSet;
+      QStringList wmsLayerList = replacementList.at( 2 ).split( "," );
+      QStringList wmsStyleList;
+      if ( replacementList.size() > 3 )
+      {
+        wmsStyleList = replacementList.at( 3 ).split( "," );
+      }
+
+      for ( int i = 0; i < wmsLayerList.size(); ++i )
+      {
+        QString styleName;
+        if ( wmsStyleList.size() > i )
+        {
+          styleName = wmsStyleList.at( i );
+        }
+        QList<QgsMapLayer*> layerList = mapLayerFromStyle( wmsLayerList.at( i ), styleName );
+        QList<QgsMapLayer*>::const_iterator mapIdIt = layerList.constBegin();
+        for ( ; mapIdIt != layerList.constEnd(); ++mapIdIt )
+        {
+          if ( *mapIdIt )
+          {
+            layerSet.push_back(( *mapIdIt )->getLayerID() );
+          }
+        }
+      }
+
+      currentMap->setLayerSet( layerSet );
+      currentMap->setKeepLayerSet( true );
+    }
+  }
+
+  //replace composer label text
+
+  return c; //soon...
+}

Modified: trunk/qgis/src/mapserver/qgsconfigparser.h
===================================================================
--- trunk/qgis/src/mapserver/qgsconfigparser.h	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgsconfigparser.h	2010-12-24 14:11:13 UTC (rev 14973)
@@ -24,6 +24,9 @@
 #include <QList>
 #include <QSet>
 
+class QgsComposition;
+class QgsComposerLabel;
+class QgsComposerMap;
 class QDomElement;
 
 /**Interface class for configuration parsing, e.g. SLD configuration or QGIS project file*/
@@ -56,6 +59,7 @@
 
     /**Sets fallback parser (does not take ownership)*/
     void setFallbackParser( QgsConfigParser* p );
+    const QgsConfigParser* fallbackParser() { return mFallbackParser; }
 
     void setScaleDenominator( double denom ) {mScaleDenominator = denom;}
 
@@ -88,6 +92,15 @@
     /**Returns information about vector attributes with hidden edit type. Key is layer id, value is a set containing the names of the hidden attributes*/
     virtual QMap< QString, QSet<QString> > hiddenAttributes() const { return QMap< QString, QSet<QString> >(); }
 
+    /**Creates a print composition, usually for a GetPrint request. Replaces map and label parameters*/
+    QgsComposition* createPrintComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, const QMap< QString, QString >& parameterMap ) const;
+
+    /**Creates a composition from the project file (probably delegated to the fallback parser)*/
+    virtual QgsComposition* initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const = 0;
+
+    /**Adds print capabilities to xml document. ParentElem usually is the <Capabilities> element*/
+    virtual void printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const = 0;
+
   protected:
     /**Parser to forward not resolved requests (e.g. SLD parser based on user request might have a fallback parser with admin configuration)*/
     QgsConfigParser* mFallbackParser;

Modified: trunk/qgis/src/mapserver/qgsgetrequesthandler.cpp
===================================================================
--- trunk/qgis/src/mapserver/qgsgetrequesthandler.cpp	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgsgetrequesthandler.cpp	2010-12-24 14:11:13 UTC (rev 14973)
@@ -107,10 +107,6 @@
       {
         formatString = "PNG";
       }
-      else
-      {
-        throw QgsMapServiceException( "InvalidFormat", "Invalid format, only jpg and png are supported" );
-      }
       mFormat = formatString;
     }
   }
@@ -301,3 +297,8 @@
   QByteArray ba = exceptionDoc.toByteArray();
   sendHttpResponse( &ba, "text/xml" );
 }
+
+void QgsGetRequestHandler::sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const
+{
+  sendHttpResponse( ba, formatString );
+}

Modified: trunk/qgis/src/mapserver/qgsgetrequesthandler.h
===================================================================
--- trunk/qgis/src/mapserver/qgsgetrequesthandler.h	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgsgetrequesthandler.h	2010-12-24 14:11:13 UTC (rev 14973)
@@ -29,4 +29,5 @@
     void sendGetFeatureInfoResponse( const QDomDocument& infoDoc, const QString& infoFormat ) const;
     void sendServiceException( const QgsMapServiceException& ex ) const;
     void sendGetStyleResponse( const QDomDocument& doc ) const;
+    void sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const;
 };

Modified: trunk/qgis/src/mapserver/qgsmslayercache.cpp
===================================================================
--- trunk/qgis/src/mapserver/qgsmslayercache.cpp	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgsmslayercache.cpp	2010-12-24 14:11:13 UTC (rev 14973)
@@ -79,6 +79,7 @@
   QMap<QPair<QString, QString>, QgsMSLayerCacheEntry>::iterator it = mEntries.find( urlNamePair );
   if ( it == mEntries.end() )
   {
+    QgsMSDebugMsg( "Layer not found in cache" )
     return 0;
   }
   else
@@ -92,6 +93,7 @@
       vl->removeOverlay( "diagram" );
     }
 #endif //DIAGRAMSERVER
+    QgsMSDebugMsg( "Layer found in cache" )
     return it->layerPointer;
   }
 }

Modified: trunk/qgis/src/mapserver/qgsprojectparser.cpp
===================================================================
--- trunk/qgis/src/mapserver/qgsprojectparser.cpp	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgsprojectparser.cpp	2010-12-24 14:11:13 UTC (rev 14973)
@@ -22,6 +22,16 @@
 #include "qgsrasterlayer.h"
 #include "qgsvectorlayer.h"
 
+#include "qgscomposition.h"
+#include "qgscomposerarrow.h"
+#include "qgscomposerattributetable.h"
+#include "qgscomposerlabel.h"
+#include "qgscomposerlegend.h"
+#include "qgscomposermap.h"
+#include "qgscomposerpicture.h"
+#include "qgscomposerscalebar.h"
+#include "qgscomposershape.h"
+
 QgsProjectParser::QgsProjectParser( QDomDocument* xmlDoc ): QgsConfigParser(), mXMLDoc( xmlDoc )
 {
   mOutputUnits = QgsMapRenderer::Millimeters;
@@ -814,3 +824,161 @@
   return legendLayerFileList.at( 0 ).toElement().attribute( "layerid" );
 }
 
+QgsComposition* QgsProjectParser::initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const
+{
+  //Create composition from xml
+  QDomElement composerElem = composerByName( composerTemplate );
+  if ( composerElem.isNull() )
+  {
+    return 0;
+  }
+
+  QDomElement compositionElem = composerElem.firstChildElement( "Composition" );
+  if ( compositionElem.isNull() )
+  {
+    return 0;
+  }
+
+  QgsComposition* composition = new QgsComposition( mapRenderer ); //set resolution, paper size from composer element attributes
+  if ( !composition->readXML( compositionElem, *mXMLDoc ) )
+  {
+    delete composition;
+    return 0;
+  }
+
+  QList<QDomElement> scaleBarElemList;
+
+  //go through all the item elements and add them to the composition (and to the lists)
+  QDomNodeList itemNodes = composerElem.childNodes();
+  for ( int i = 0; i < itemNodes.size(); ++i )
+  {
+    QDomElement currentElem = itemNodes.at( i ).toElement();
+    QString elemName = currentElem.tagName();
+    if ( elemName == "ComposerMap" )
+    {
+      QgsComposerMap* map = new QgsComposerMap( composition );
+      map->readXML( currentElem, *mXMLDoc );
+      composition->addItem( map );
+      mapList.push_back( map );
+    }
+    else if ( elemName == "ComposerLabel" )
+    {
+      QgsComposerLabel* label = new QgsComposerLabel( composition );
+      label->readXML( currentElem, *mXMLDoc );
+      composition->addItem( label );
+      labelList.push_back( label );
+    }
+    else if ( elemName == "ComposerLegend" )
+    {
+      QgsComposerLegend* legend = new QgsComposerLegend( composition );
+      legend->readXML( currentElem, *mXMLDoc );
+      composition->addItem( legend );
+    }
+    else if ( elemName == "ComposerPicture" )
+    {
+      QgsComposerPicture* picture = new QgsComposerPicture( composition );
+      picture->readXML( currentElem, *mXMLDoc );
+      composition->addItem( picture );
+    }
+    else if ( elemName == "ComposerScaleBar" )
+    {
+      //scalebars need to be loaded after the composer maps
+      scaleBarElemList.push_back( currentElem );
+    }
+    else if ( elemName == "ComposerShape" )
+    {
+      QgsComposerShape* shape = new QgsComposerShape( composition );
+      shape->readXML( currentElem, *mXMLDoc );
+      composition->addItem( shape );
+    }
+    else if ( elemName == "ComposerArrow" )
+    {
+      QgsComposerArrow* arrow = new QgsComposerArrow( composition );
+      arrow->readXML( currentElem, *mXMLDoc );
+      composition->addItem( arrow );
+    }
+    else if ( elemName == "ComposerAttributeTable" )
+    {
+      QgsComposerAttributeTable* table = new QgsComposerAttributeTable( composition );
+      table->readXML( currentElem, *mXMLDoc );
+      composition->addItem( table );
+    }
+  }
+
+  //scalebars need to be loaded after the composer maps to receive the correct size
+  QList<QDomElement>::const_iterator scaleBarIt = scaleBarElemList.constBegin();
+  for ( ; scaleBarIt != scaleBarElemList.constEnd(); ++scaleBarIt )
+  {
+    QgsComposerScaleBar* bar = new QgsComposerScaleBar( composition );
+    bar->readXML( *scaleBarIt, *mXMLDoc );
+    composition->addItem( bar );
+  }
+
+  return composition;
+}
+
+void QgsProjectParser::printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const
+{
+  if ( !mXMLDoc )
+  {
+    return;
+  }
+
+  QDomNodeList composerNodeList = mXMLDoc->elementsByTagName( "Composer" );
+  for ( int i = 0; i < composerNodeList.size(); ++i )
+  {
+    QDomElement composerTemplateElem = doc.createElement( "ComposerTemplate" );
+    QDomElement currentComposerElem = composerNodeList.at( i ).toElement();
+    if ( currentComposerElem.isNull() )
+    {
+      continue;
+    }
+
+    composerTemplateElem.setAttribute( "name", currentComposerElem.attribute( "title" ) );
+    composerTemplateElem.setAttribute( "xmlns:wms", "http://www.opengis.net/wms" );
+    composerTemplateElem.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
+    composerTemplateElem.setAttribute( "xsi:type", "wms:_ExtendedCapabilities" );
+
+    //add available composer maps and their size in mm
+    QDomNodeList composerMapList = currentComposerElem.elementsByTagName( "ComposerMap" );
+    for ( int j = 0; j < composerMapList.size(); ++j )
+    {
+      QDomElement cmap = composerMapList.at( j ).toElement();
+      QDomElement citem = cmap.firstChildElement( "ComposerItem" );
+      if ( citem.isNull() )
+      {
+        continue;
+      }
+
+      QDomElement composerMapElem = doc.createElement( "ComposerMap" );
+      composerMapElem.setAttribute( "name", "map" + cmap.attribute( "id" ) );
+      composerMapElem.setAttribute( "width", citem.attribute( "width" ) );
+      composerMapElem.setAttribute( "height", citem.attribute( "height" ) );
+      composerTemplateElem.appendChild( composerMapElem );
+    }
+
+    parentElement.appendChild( composerTemplateElem );
+  }
+}
+
+QDomElement QgsProjectParser::composerByName( const QString& composerName ) const
+{
+  QDomElement composerElem;
+  if ( !mXMLDoc )
+  {
+    return composerElem;
+  }
+
+  QDomNodeList composerNodeList = mXMLDoc->elementsByTagName( "Composer" );
+  for ( int i = 0; i < composerNodeList.size(); ++i )
+  {
+    QDomElement currentComposerElem = composerNodeList.at( i ).toElement();
+    if ( currentComposerElem.attribute( "title" ) == composerName )
+    {
+      return currentComposerElem;
+    }
+  }
+
+  return composerElem;
+}
+

Modified: trunk/qgis/src/mapserver/qgsprojectparser.h
===================================================================
--- trunk/qgis/src/mapserver/qgsprojectparser.h	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgsprojectparser.h	2010-12-24 14:11:13 UTC (rev 14973)
@@ -85,6 +85,14 @@
     /**Return project title*/
     QString projectTitle() const;
 
+    const QDomDocument* xmlDoc() const { return mXMLDoc; }
+
+    /**Creates a composition from the project file (probably delegated to the fallback parser)*/
+    QgsComposition* initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const;
+
+    /**Adds print capabilities to xml document. ParentElem usually is the <Capabilities> element*/
+    void printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
+
   private:
     /**Content of project file*/
     QDomDocument* mXMLDoc;
@@ -118,6 +126,9 @@
                     const QStringList &nonIdentifiableLayers,
                     const QgsRectangle &mapExtent,
                     const QgsCoordinateReferenceSystem &mapCRS ) const;
+
+    /**Returns dom element of composer (identified by composer title) or a null element in case of error*/
+    QDomElement composerByName( const QString& composerName ) const;
 };
 
 #endif // QGSPROJECTPARSER_H

Modified: trunk/qgis/src/mapserver/qgsrequesthandler.h
===================================================================
--- trunk/qgis/src/mapserver/qgsrequesthandler.h	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgsrequesthandler.h	2010-12-24 14:11:13 UTC (rev 14973)
@@ -40,6 +40,7 @@
     virtual void sendGetFeatureInfoResponse( const QDomDocument& infoDoc, const QString& infoFormat ) const = 0;
     virtual void sendServiceException( const QgsMapServiceException& ex ) const = 0;
     virtual void sendGetStyleResponse( const QDomDocument& doc ) const = 0;
+    virtual void sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const = 0;
   protected:
     /**This is set by the parseInput methods of the subclasses (parameter FORMAT, e.g. 'FORMAT=PNG')*/
     QString mFormat;

Modified: trunk/qgis/src/mapserver/qgssldparser.cpp
===================================================================
--- trunk/qgis/src/mapserver/qgssldparser.cpp	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgssldparser.cpp	2010-12-24 14:11:13 UTC (rev 14973)
@@ -1539,6 +1539,23 @@
   }
 }
 
+QgsComposition* QgsSLDParser::initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const
+{
+  if ( mFallbackParser )
+  {
+    return mFallbackParser->initComposition( composerTemplate, mapRenderer, mapList, labelList );
+  }
+  return 0;
+}
+
+void QgsSLDParser::printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const
+{
+  if ( mFallbackParser )
+  {
+    mFallbackParser->printCapabilities( parentElement, doc );
+  }
+}
+
 #ifdef DIAGRAMSERVER
 int QgsSLDParser::overlaysFromUserStyle( const QDomElement& userStyleElement, QgsVectorLayer* vec ) const
 {

Modified: trunk/qgis/src/mapserver/qgssldparser.h
===================================================================
--- trunk/qgis/src/mapserver/qgssldparser.h	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgssldparser.h	2010-12-24 14:11:13 UTC (rev 14973)
@@ -68,6 +68,12 @@
 
     virtual void setParameterMap( const std::map<QString, QString>& parameterMap ) { mParameterMap = parameterMap; }
 
+    /**Creates a composition from the project file (delegated to the fallback parser)*/
+    QgsComposition* initComposition( const QString& composerTemplate, QgsMapRenderer* mapRenderer, QList< QgsComposerMap*>& mapList, QList< QgsComposerLabel* > labelList ) const;
+
+    /**Adds print capabilities to xml document. Delegated to fallback parser*/
+    void printCapabilities( QDomElement& parentElement, QDomDocument& doc ) const;
+
   private:
     /**Don't use the default constructor*/
     QgsSLDParser();

Modified: trunk/qgis/src/mapserver/qgssoaprequesthandler.cpp
===================================================================
--- trunk/qgis/src/mapserver/qgssoaprequesthandler.cpp	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgssoaprequesthandler.cpp	2010-12-24 14:11:13 UTC (rev 14973)
@@ -469,8 +469,11 @@
   sendHttpResponse( &ba, "text/xml" );
 }
 
+void QgsSOAPRequestHandler::sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const
+{
+  //soon...
+}
 
-
 void QgsSOAPRequestHandler::sendServiceException( const QgsMapServiceException& ex ) const
 {
   //create response document

Modified: trunk/qgis/src/mapserver/qgssoaprequesthandler.h
===================================================================
--- trunk/qgis/src/mapserver/qgssoaprequesthandler.h	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgssoaprequesthandler.h	2010-12-24 14:11:13 UTC (rev 14973)
@@ -34,6 +34,7 @@
     void sendGetFeatureInfoResponse( const QDomDocument& infoDoc, const QString& infoFormat ) const;
     void sendServiceException( const QgsMapServiceException& ex ) const;
     void sendGetStyleResponse( const QDomDocument& doc ) const;
+    void sendGetPrintResponse( QByteArray* ba, const QString& formatString ) const;
   private:
     /**Parses the xml of a getMap request and fills the parameters into the map. Returns 0 in case of success*/
     int parseGetMapElement( std::map<QString, QString>& parameterMap, const QDomElement& getMapElement ) const;

Modified: trunk/qgis/src/mapserver/qgswmsserver.cpp
===================================================================
--- trunk/qgis/src/mapserver/qgswmsserver.cpp	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgswmsserver.cpp	2010-12-24 14:11:13 UTC (rev 14973)
@@ -45,6 +45,12 @@
 #include <QTextStream>
 #include <QDir>
 
+//for printing
+#include "qgscomposition.h"
+#include <QBuffer>
+#include <QPrinter>
+#include <QSvgGenerator>
+
 QgsWMSServer::QgsWMSServer( std::map<QString, QString> parameters, QgsMapRenderer* renderer )
     : mParameterMap( parameters )
     , mConfigParser( 0 )
@@ -208,18 +214,27 @@
   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
   QgsMSDebugMsg( "calling layersAndStylesCapabilities" )
-  mConfigParser->layersAndStylesCapabilities( capabilityElement, doc );
+  if ( mConfigParser )
+  {
+    mConfigParser->layersAndStylesCapabilities( capabilityElement, doc );
+  }
   QgsMSDebugMsg( "layersAndStylesCapabilities returned" )
 
   //for debugging: save the document to disk
-  QFile capabilitiesFile( QDir::tempPath() + "/capabilities.txt" );
+  /*QFile capabilitiesFile( QDir::tempPath() + "/capabilities.txt" );
   if ( capabilitiesFile.open( QIODevice::WriteOnly | QIODevice::Text ) )
   {
     QTextStream capabilitiesStream( &capabilitiesFile );
     doc.save( capabilitiesStream, 4 );
-  }
+  }*/
   return doc;
 }
 
@@ -340,81 +355,104 @@
   return mConfigParser->getStyle( styleName, layerName );
 }
 
-QImage* QgsWMSServer::getMap()
+QByteArray* QgsWMSServer::getPrint( QString& formatString )
 {
-  QgsMSDebugMsg( "Entering" )
-  if ( !mConfigParser )
+  QStringList layersList, stylesList, layerIdList;
+  QImage* theImage = initializeRendering( layersList, stylesList, layerIdList, formatString );
+  if ( !theImage )
   {
-    QgsMSDebugMsg( "Error: mSLDParser is 0" )
     return 0;
   }
+  delete theImage;
 
-  if ( !mMapRenderer )
+  //GetPrint request needs a template parameter
+  std::map<QString, QString>::const_iterator templateIt = mParameterMap.find( "TEMPLATE" );
+  if ( templateIt == mParameterMap.end() )
   {
-    QgsMSDebugMsg( "Error: mMapRenderer is 0" )
     return 0;
   }
 
-  QStringList layersList, stylesList;
-  QString formatString;
-
-  if ( readLayersAndStyles( layersList, stylesList ) != 0 )
+  QgsComposition* c = mConfigParser->createPrintComposition( templateIt->second, mMapRenderer, QMap<QString, QString>( mParameterMap ) );
+  if ( !c )
   {
-    QgsMSDebugMsg( "error reading layers and styles" )
     return 0;
   }
 
-  if ( initializeSLDParser( layersList, stylesList ) != 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 )
   {
-    return 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 );
+    c->render( &p, targetArea, sourceArea );
+    p.end();
   }
-
-  //pass external GML to the SLD parser.
-  std::map<QString, QString>::const_iterator gmlIt = mParameterMap.find( "GML" );
-  if ( gmlIt != mParameterMap.end() )
+  else if ( formatString.compare( "png", Qt::CaseInsensitive ) == 0 )
   {
-    QDomDocument* gmlDoc = new QDomDocument();
-    if ( gmlDoc->setContent( gmlIt->second, true ) )
+    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( 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();
+    ba = new QByteArray();
+    QBuffer buffer( ba );
+    buffer.open( QIODevice::WriteOnly );
+    image.save( &buffer, "png", -1 );
+  }
+  else if ( formatString.compare( "pdf", Qt::CaseInsensitive ) == 0 )
+  {
+    QTemporaryFile tempFile;
+    if ( !tempFile.open() )
     {
-      QString layerName = gmlDoc->documentElement().attribute( "layerName" );
-      QgsMSDebugMsg( "Adding entry with key: " + layerName + " to external GML data" )
-      mConfigParser->addExternalGMLData( layerName, gmlDoc );
+      delete c;
+      return 0;
     }
-    else
-    {
-      QgsMSDebugMsg( "Error, could not add external GML to QgsSLDParser" )
-      delete gmlDoc;
-    }
-  }
 
-  //get output format
-  std::map<QString, QString>::const_iterator outIt = mParameterMap.find( "FORMAT" );
-  if ( outIt == mParameterMap.end() )
-  {
-    QgsMSDebugMsg( "Error, no parameter FORMAT found" )
-    return 0; //output format parameter also mandatory
-  }
+    QPrinter printer;
+    printer.setOutputFormat( QPrinter::PdfFormat );
+    printer.setOutputFileName( tempFile.fileName() );
+    printer.setPaperSize( QSizeF( c->paperWidth(), c->paperHeight() ), QPrinter::Millimeter );
+    printer.setResolution( c->printResolution() );
 
-  QImage* theImage = createImage();
-  if ( !theImage )
-  {
-    return 0;
-  }
+    QRectF paperRectMM = printer.pageRect( QPrinter::Millimeter );
+    QRectF paperRectPixel = printer.pageRect( QPrinter::DevicePixel );
+    QPainter p( &printer );
+    c->render( &p, paperRectPixel, paperRectMM );
+    p.end();
 
-  if ( configureMapRender( theImage ) != 0 )
-  {
-    return 0;
+    ba = new QByteArray();
+    *ba = tempFile.readAll();
   }
-  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->destinationSrs().mapUnits() );
-  QgsRectangle mapExtent = mMapRenderer->extent();
-  mConfigParser->setScaleDenominator( scaleCalc.calculate( mapExtent, theImage->width() ) );
+  delete c;
+  return ba;
+}
 
-  //create objects for qgis rendering
-  QStringList theLayers = layerSet( layersList, stylesList, mMapRenderer->destinationSrs() );
-  mMapRenderer->setLayerSet( theLayers );
+QImage* QgsWMSServer::getMap()
+{
+  QStringList layersList, stylesList, layerIdList;
+  QString outputFormat;
+  QImage* theImage = initializeRendering( layersList, stylesList, layerIdList, outputFormat );
 
   QPainter thePainter( theImage );
   thePainter.setRenderHint( QPainter::Antialiasing ); //make it look nicer
@@ -610,6 +648,82 @@
   return 0;
 }
 
+QImage* QgsWMSServer::initializeRendering( QStringList& layersList, QStringList& stylesList, QStringList& layerIdList, QString& outputFormat )
+{
+  if ( !mConfigParser )
+  {
+    QgsMSDebugMsg( "Error: mSLDParser is 0" )
+    return 0;
+  }
+
+  if ( !mMapRenderer )
+  {
+    QgsMSDebugMsg( "Error: mMapRenderer is 0" )
+    return 0;
+  }
+
+  if ( readLayersAndStyles( layersList, stylesList ) != 0 )
+  {
+    QgsMSDebugMsg( "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" );
+      QgsMSDebugMsg( "Adding entry with key: " + layerName + " to external GML data" )
+      mConfigParser->addExternalGMLData( layerName, gmlDoc );
+    }
+    else
+    {
+      QgsMSDebugMsg( "Error, could not add external GML to QgsSLDParser" )
+      delete gmlDoc;
+    }
+  }
+
+  //get output format
+  std::map<QString, QString>::const_iterator outIt = mParameterMap.find( "FORMAT" );
+  if ( outIt == mParameterMap.end() )
+  {
+    QgsMSDebugMsg( "Error, no parameter FORMAT found" )
+    return 0; //output format parameter also mandatory
+  }
+  outputFormat = outIt->second;
+
+  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->destinationSrs().mapUnits() );
+  QgsRectangle mapExtent = mMapRenderer->extent();
+  mConfigParser->setScaleDenominator( scaleCalc.calculate( mapExtent, theImage->width() ) );
+
+  //create objects for qgis rendering
+  QStringList theLayers = layerSet( layersList, stylesList, mMapRenderer->destinationSrs() );
+  mMapRenderer->setLayerSet( theLayers );
+  return theImage;
+}
+
 QImage* QgsWMSServer::createImage( int width, int height ) const
 {
   bool conversionSuccess;
@@ -619,12 +733,12 @@
     std::map<QString, QString>::const_iterator wit = mParameterMap.find( "WIDTH" );
     if ( wit == mParameterMap.end() )
     {
-      return 0; //width parameter is mandatory
+      width = 0; //width parameter is mandatory
     }
     width = wit->second.toInt( &conversionSuccess );
     if ( !conversionSuccess )
     {
-      return 0;
+      width = 0;
     }
   }
 
@@ -633,12 +747,12 @@
     std::map<QString, QString>::const_iterator hit = mParameterMap.find( "HEIGHT" );
     if ( hit == mParameterMap.end() )
     {
-      return 0; //height parameter is mandatory
+      height = 0; //height parameter is mandatory
     }
     height = hit->second.toInt( &conversionSuccess );
     if ( !conversionSuccess )
     {
-      return 0;
+      height = 0;
     }
   }
 
@@ -718,23 +832,27 @@
   std::map<QString, QString>::const_iterator bbIt = mParameterMap.find( "BBOX" );
   if ( bbIt == mParameterMap.end() )
   {
-    return 2;
+    //GetPrint request is allowed to have missing BBOX parameter
+    minx = 0; miny = 0; maxx = 0; maxy = 0;
   }
-  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;}
+  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" );
+    if ( !bboxOk )
+    {
+      //throw a service exception
+      throw QgsMapServiceException( "InvalidParameterValue", "Invalid BBOX parameter" );
+    }
   }
 
   QGis::UnitType mapUnits = QGis::Degrees;

Modified: trunk/qgis/src/mapserver/qgswmsserver.h
===================================================================
--- trunk/qgis/src/mapserver/qgswmsserver.h	2010-12-24 07:14:58 UTC (rev 14972)
+++ trunk/qgis/src/mapserver/qgswmsserver.h	2010-12-24 14:11:13 UTC (rev 14973)
@@ -60,6 +60,11 @@
     /**Returns an SLD file with the style of the requested layer. Exception is raised in case of troubles :-)*/
     QDomDocument getStyle();
 
+    /**Returns printed page as binary
+      @param formatString out: format of the print output (e.g. pdf, svg, png, ...)
+      @return printed page as binary or 0 in case of error*/
+    QByteArray* getPrint( QString& formatString );
+
     /**Creates an xml document that describes the result of the getFeatureInfo request.
        @return 0 in case of success*/
     int getFeatureInfo( QDomDocument& result );
@@ -70,6 +75,15 @@
   private:
     /**Don't use the default constructor*/
     QgsWMSServer();
+
+    /**Initializes WMS layers and configures mMapRendering.
+      @param layersList out: list with WMS layer names
+      @param stylesList out: list with WMS style names
+      @param layerIdList out: list with QGIS layer ids
+      @param outputFormat out: name of requested output format
+      @return image configured together with mMapRenderer (or 0 in case of error). The calling function takes ownership of the image*/
+    QImage* initializeRendering( QStringList& layersList, QStringList& stylesList, QStringList& layerIdList, QString& outputFormat );
+
     /**Creates a QImage from the HEIGHT and WIDTH parameters
      @param width image width (or -1 if width should be taken from WIDTH wms parameter)
      @param height image height (or -1 if height should be taken from HEIGHT wms parameter)



More information about the QGIS-commit mailing list