[layout][server] composer -> layout in getprint
@ -78,19 +78,24 @@
|
||||
|
||||
//for printing
|
||||
#include "qgslayoutmanager.h"
|
||||
#include "qgscomposition.h"
|
||||
#include "qgscomposerarrow.h"
|
||||
#include "qgscomposerlabel.h"
|
||||
#include "qgscomposerlegend.h"
|
||||
#include "qgscomposermap.h"
|
||||
#include "qgscomposermapgrid.h"
|
||||
#include "qgscomposerframe.h"
|
||||
#include "qgscomposerhtml.h"
|
||||
#include "qgscomposerpicture.h"
|
||||
#include "qgscomposerscalebar.h"
|
||||
#include "qgscomposershape.h"
|
||||
#include "qgslayoutexporter.h"
|
||||
#include "qgslayoutsize.h"
|
||||
#include "qgslayoutmeasurement.h"
|
||||
#include "qgsprintlayout.h"
|
||||
#include "qgslayoutpagecollection.h"
|
||||
#include "qgslayoutitempage.h"
|
||||
#include "qgslayoutitemlabel.h"
|
||||
#include "qgslayoutitemlegend.h"
|
||||
#include "qgslayoutitemmap.h"
|
||||
#include "qgslayoutitemmapgrid.h"
|
||||
#include "qgslayoutframe.h"
|
||||
#include "qgslayoutitemhtml.h"
|
||||
#include "qgslayoutitempicture.h"
|
||||
#include "qgslayoutitemscalebar.h"
|
||||
#include "qgslayoutitemshape.h"
|
||||
#include "qgsfeaturefilterprovidergroup.h"
|
||||
#include "qgsogcutils.h"
|
||||
#include "qgsunittypes.h"
|
||||
#include <QBuffer>
|
||||
#include <QPrinter>
|
||||
#include <QSvgGenerator>
|
||||
@ -295,7 +300,8 @@ namespace QgsWms
|
||||
QList<QgsMapLayer *> layers;
|
||||
QList<QgsWmsParametersLayer> params = mWmsParameters.layersParameters();
|
||||
|
||||
// create the output image
|
||||
// create the output image (this is not really used but configureMapSettings
|
||||
// needs it)
|
||||
std::unique_ptr<QImage> image( new QImage() );
|
||||
|
||||
// configure map settings (background, DPI, ...)
|
||||
@ -354,69 +360,85 @@ namespace QgsWms
|
||||
mapSettings.setLayers( layers );
|
||||
|
||||
const QgsLayoutManager *lManager = mProject->layoutManager();
|
||||
QDomDocument cDocument;
|
||||
if ( !lManager->saveAsTemplate( templateName, cDocument ) )
|
||||
QgsPrintLayout *sourceLayout( dynamic_cast<QgsPrintLayout *>( lManager->layoutByName( templateName ) ) );
|
||||
if ( !sourceLayout )
|
||||
{
|
||||
throw QgsBadRequestException( QStringLiteral( "InvalidTemplate" ),
|
||||
QStringLiteral( "Template '%1' is not known" ).arg( templateName ) );
|
||||
}
|
||||
|
||||
QgsComposition *c = new QgsComposition( const_cast<QgsProject *>( mProject ) );
|
||||
if ( !c->loadFromTemplate( cDocument, nullptr, false, true ) )
|
||||
// Check that layout has at least one page
|
||||
if ( sourceLayout->pageCollection()->pageCount() < 1 )
|
||||
{
|
||||
throw QgsBadRequestException( QStringLiteral( "InvalidTemplate" ),
|
||||
QStringLiteral( "Template '%1' is not loaded correctly" ).arg( templateName ) );
|
||||
QStringLiteral( "Template '%1' has no pages" ).arg( templateName ) );
|
||||
}
|
||||
configureComposition( c, mapSettings );
|
||||
|
||||
c->setPlotStyle( QgsComposition::Print );
|
||||
std::unique_ptr<QgsPrintLayout> layout( sourceLayout->clone() );
|
||||
|
||||
QByteArray ba;
|
||||
configurePrintLayout( layout.get(), mapSettings );
|
||||
|
||||
// Get the temporary output file
|
||||
QTemporaryFile tempOutputFile( QStringLiteral( "XXXXXX.%1" ).arg( formatString.toLower() ) );
|
||||
if ( !tempOutputFile.open() )
|
||||
{
|
||||
// let the caller handle this
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//SVG export without a running X-Server is a problem. See e.g. http://developer.qt.nokia.com/forums/viewthread/2038
|
||||
if ( formatString.compare( QLatin1String( "svg" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
c->setPlotStyle( QgsComposition::Print );
|
||||
|
||||
QSvgGenerator generator;
|
||||
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 );
|
||||
if ( c->printAsRaster() ) //embed one raster into the svg
|
||||
// Settings for the layout exporter
|
||||
QgsLayoutExporter::SvgExportSettings exportSettings;
|
||||
if ( !mWmsParameters.dpi().isEmpty() )
|
||||
{
|
||||
QImage img = c->printPageAsRaster( 0 );
|
||||
p.drawImage( QRect( 0, 0, width, height ), img, QRectF( 0, 0, img.width(), img.height() ) );
|
||||
bool ok;
|
||||
double dpi( mWmsParameters.dpi().toDouble( &ok ) );
|
||||
if ( ok )
|
||||
exportSettings.dpi = dpi;
|
||||
}
|
||||
else
|
||||
{
|
||||
c->renderPage( &p, 0 );
|
||||
}
|
||||
p.end();
|
||||
QgsLayoutExporter exporter( layout.get() );
|
||||
exporter.exportToSvg( tempOutputFile.fileName(), exportSettings );
|
||||
}
|
||||
else if ( formatString.compare( QLatin1String( "png" ), Qt::CaseInsensitive ) == 0 || formatString.compare( QLatin1String( "jpg" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
QImage image = c->printPageAsRaster( 0 ); //can only return the first page if pixmap is requested
|
||||
|
||||
QBuffer buffer( &ba );
|
||||
buffer.open( QIODevice::WriteOnly );
|
||||
image.save( &buffer, formatString.toLocal8Bit().data(), -1 );
|
||||
// Settings for the layout exporter
|
||||
QgsLayoutExporter::ImageExportSettings exportSettings;
|
||||
// Get the dpi from input or use the default
|
||||
double dpi( layout->renderContext().dpi( ) );
|
||||
if ( !mWmsParameters.dpi().isEmpty() )
|
||||
{
|
||||
bool ok;
|
||||
double _dpi = mWmsParameters.dpi().toDouble( &ok );
|
||||
if ( ! ok )
|
||||
dpi = _dpi;
|
||||
}
|
||||
exportSettings.dpi = dpi;
|
||||
// Destination image size in px
|
||||
QgsLayoutSize layoutSize( layout->pageCollection()->page( 0 )->sizeWithUnits() );
|
||||
QgsLayoutMeasurement width( layout->convertFromLayoutUnits( layoutSize.width(), QgsUnitTypes::LayoutUnit::LayoutMillimeters ) );
|
||||
QgsLayoutMeasurement height( layout->convertFromLayoutUnits( layoutSize.height(), QgsUnitTypes::LayoutUnit::LayoutMillimeters ) );
|
||||
exportSettings.imageSize = QSize( ( int )( width.length() * dpi / 25.4 ), ( int )( height.length() * dpi / 25.4 ) );
|
||||
// Export first page only (unless it's a pdf, see below)
|
||||
exportSettings.pages.append( 0 );
|
||||
QgsLayoutExporter exporter( layout.get() );
|
||||
exporter.exportToImage( tempOutputFile.fileName(), exportSettings );
|
||||
}
|
||||
else if ( formatString.compare( QLatin1String( "pdf" ), Qt::CaseInsensitive ) == 0 )
|
||||
{
|
||||
QTemporaryFile tempFile;
|
||||
if ( !tempFile.open() )
|
||||
// Settings for the layout exporter
|
||||
QgsLayoutExporter::PdfExportSettings exportSettings;
|
||||
// TODO: handle size from input ?
|
||||
if ( !mWmsParameters.dpi().isEmpty() )
|
||||
{
|
||||
delete c;
|
||||
return nullptr;
|
||||
bool ok;
|
||||
double dpi( mWmsParameters.dpi().toDouble( &ok ) );
|
||||
if ( ok )
|
||||
exportSettings.dpi = dpi;
|
||||
}
|
||||
|
||||
c->exportAsPDF( tempFile.fileName() );
|
||||
ba = tempFile.readAll();
|
||||
// Export all pages
|
||||
QgsLayoutExporter exporter( layout.get() );
|
||||
exporter.exportToPdf( tempOutputFile.fileName(), exportSettings );
|
||||
}
|
||||
else //unknown format
|
||||
{
|
||||
@ -424,214 +446,180 @@ namespace QgsWms
|
||||
QStringLiteral( "Output format '%1' is not supported in the GetPrint request" ).arg( formatString ) );
|
||||
}
|
||||
|
||||
delete c;
|
||||
return ba;
|
||||
return tempOutputFile.readAll();
|
||||
}
|
||||
|
||||
bool QgsRenderer::configureComposition( QgsComposition *c, const QgsMapSettings &mapSettings )
|
||||
bool QgsRenderer::configurePrintLayout( QgsPrintLayout *c, const QgsMapSettings &mapSettings )
|
||||
{
|
||||
if ( !mWmsParameters.dpi().isEmpty() )
|
||||
c->setPrintResolution( mWmsParameters.dpiAsInt() );
|
||||
|
||||
// Composer items
|
||||
QList<QgsComposerItem * > itemList;
|
||||
c->composerItems( itemList );
|
||||
// Composer legends have to be updated after the other items
|
||||
QList<QgsComposerLegend *> composerLegends;
|
||||
|
||||
// Update composer items
|
||||
QList<QgsComposerItem *>::iterator itemIt = itemList.begin();
|
||||
for ( ; itemIt != itemList.end(); ++itemIt )
|
||||
// Maps are configured first
|
||||
QList<QgsLayoutItemMap *> maps;
|
||||
c->layoutItems<QgsLayoutItemMap>( maps );
|
||||
// Layout maps now use a string UUID as "id", let's assume that the first map
|
||||
// has id 0 and so on ...
|
||||
int mapId = 0;
|
||||
for ( auto &map : maps )
|
||||
{
|
||||
// firstly the composer maps
|
||||
if ( ( *itemIt )->type() == QgsComposerItem::ComposerMap )
|
||||
QgsWmsParametersComposerMap cMapParams = mWmsParameters.composerMapParameters( mapId );
|
||||
mapId++;
|
||||
|
||||
//map extent is mandatory
|
||||
if ( !cMapParams.mHasExtent )
|
||||
{
|
||||
QgsComposerMap *map = qobject_cast< QgsComposerMap *>( *itemIt );
|
||||
if ( !map )
|
||||
continue;
|
||||
//remove map from composition if not referenced by the request
|
||||
c->removeItem( map );
|
||||
delete map;
|
||||
continue;
|
||||
}
|
||||
// Change CRS of map set to "project CRS" to match requested CRS
|
||||
// (if map has a valid preset crs then we keep this crs and don't use the
|
||||
// requested crs for this map item)
|
||||
if ( mapSettings.destinationCrs().isValid() && !map->presetCrs().isValid() )
|
||||
map->setCrs( mapSettings.destinationCrs() );
|
||||
|
||||
QgsWmsParametersComposerMap cMapParams = mWmsParameters.composerMapParameters( map->id() );
|
||||
QgsRectangle r( cMapParams.mExtent );
|
||||
if ( mWmsParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) &&
|
||||
mapSettings.destinationCrs().hasAxisInverted() )
|
||||
{
|
||||
r.invert();
|
||||
}
|
||||
map->setExtent( r );
|
||||
|
||||
//map extent is mandatory
|
||||
if ( !cMapParams.mHasExtent )
|
||||
// scale
|
||||
if ( cMapParams.mScale > 0 )
|
||||
{
|
||||
map->setScale( cMapParams.mScale );
|
||||
}
|
||||
|
||||
// rotation
|
||||
if ( cMapParams.mRotation )
|
||||
{
|
||||
map->setMapRotation( cMapParams.mRotation );
|
||||
}
|
||||
|
||||
if ( !map->keepLayerSet() )
|
||||
{
|
||||
if ( cMapParams.mLayers.isEmpty() )
|
||||
{
|
||||
//remove map from composition if not referenced by the request
|
||||
c->removeItem( map );
|
||||
delete map;
|
||||
continue;
|
||||
map->setLayers( mapSettings.layers() );
|
||||
}
|
||||
|
||||
// Change CRS of map set to "project CRS" to match requested CRS
|
||||
// (if map has a valid preset crs then we keep this crs and don't use the
|
||||
// requested crs for this map item)
|
||||
if ( mapSettings.destinationCrs().isValid() && !map->presetCrs().isValid() )
|
||||
map->setCrs( mapSettings.destinationCrs() );
|
||||
|
||||
QgsRectangle r( cMapParams.mExtent );
|
||||
if ( mWmsParameters.versionAsNumber() >= QgsProjectVersion( 1, 3, 0 ) && mapSettings.destinationCrs().hasAxisInverted() )
|
||||
else
|
||||
{
|
||||
r.invert();
|
||||
}
|
||||
map->setNewExtent( r );
|
||||
|
||||
// scale
|
||||
if ( cMapParams.mScale > 0 )
|
||||
{
|
||||
map->setNewScale( cMapParams.mScale );
|
||||
}
|
||||
|
||||
// rotation
|
||||
if ( cMapParams.mRotation )
|
||||
{
|
||||
map->setMapRotation( cMapParams.mRotation );
|
||||
}
|
||||
|
||||
if ( !map->keepLayerSet() )
|
||||
{
|
||||
if ( cMapParams.mLayers.isEmpty() )
|
||||
QList<QgsMapLayer *> layerSet;
|
||||
const QList<QgsWmsParametersLayer> layers = cMapParams.mLayers;
|
||||
for ( const auto &layer : layers )
|
||||
{
|
||||
map->setLayers( mapSettings.layers() );
|
||||
}
|
||||
else
|
||||
{
|
||||
QList<QgsMapLayer *> layerSet;
|
||||
QList<QgsWmsParametersLayer> layers = cMapParams.mLayers;
|
||||
Q_FOREACH ( QgsWmsParametersLayer layer, layers )
|
||||
QString nickname = layer.mNickname;
|
||||
QString style = layer.mStyle;
|
||||
if ( mNicknameLayers.contains( nickname ) && !mRestrictedLayers.contains( nickname ) )
|
||||
{
|
||||
QString nickname = layer.mNickname;
|
||||
QString style = layer.mStyle;
|
||||
if ( mNicknameLayers.contains( nickname ) && !mRestrictedLayers.contains( nickname ) )
|
||||
if ( !style.isEmpty() )
|
||||
{
|
||||
if ( !style.isEmpty() )
|
||||
bool rc = mNicknameLayers[nickname]->styleManager()->setCurrentStyle( style );
|
||||
if ( ! rc )
|
||||
{
|
||||
bool rc = mNicknameLayers[nickname]->styleManager()->setCurrentStyle( style );
|
||||
if ( ! rc )
|
||||
{
|
||||
throw QgsMapServiceException( QStringLiteral( "StyleNotDefined" ), QStringLiteral( "Style \"%1\" does not exist for layer \"%2\"" ).arg( style, nickname ) );
|
||||
}
|
||||
throw QgsMapServiceException( QStringLiteral( "StyleNotDefined" ), QStringLiteral( "Style \"%1\" does not exist for layer \"%2\"" ).arg( style, nickname ) );
|
||||
}
|
||||
layerSet << mNicknameLayers[nickname];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw QgsBadRequestException( QStringLiteral( "LayerNotDefined" ),
|
||||
QStringLiteral( "Layer \"%1\" does not exist" ).arg( nickname ) );
|
||||
}
|
||||
layerSet << mNicknameLayers[nickname];
|
||||
}
|
||||
else
|
||||
{
|
||||
throw QgsBadRequestException( QStringLiteral( "LayerNotDefined" ),
|
||||
QStringLiteral( "Layer \"%1\" does not exist" ).arg( nickname ) );
|
||||
}
|
||||
layerSet << highlightLayers( cMapParams.mHighlightLayers );
|
||||
std::reverse( layerSet.begin(), layerSet.end() );
|
||||
map->setLayers( layerSet );
|
||||
}
|
||||
map->setKeepLayerSet( true );
|
||||
layerSet << highlightLayers( cMapParams.mHighlightLayers );
|
||||
std::reverse( layerSet.begin(), layerSet.end() );
|
||||
map->setLayers( layerSet );
|
||||
}
|
||||
|
||||
//grid space x / y
|
||||
if ( cMapParams.mGridX > 0 && cMapParams.mGridY > 0 )
|
||||
{
|
||||
map->grid()->setIntervalX( cMapParams.mGridX );
|
||||
map->grid()->setIntervalY( cMapParams.mGridY );
|
||||
}
|
||||
continue;
|
||||
map->setKeepLayerSet( true );
|
||||
}
|
||||
// secondly the composer legends
|
||||
else if ( ( *itemIt )->type() == QgsComposerItem::ComposerLegend )
|
||||
|
||||
//grid space x / y
|
||||
if ( cMapParams.mGridX > 0 && cMapParams.mGridY > 0 )
|
||||
{
|
||||
QgsComposerLegend *legend = qobject_cast< QgsComposerLegend *>( *itemIt );
|
||||
if ( !legend )
|
||||
continue;
|
||||
|
||||
composerLegends.push_back( legend );
|
||||
continue;
|
||||
}
|
||||
// thirdly the composer labels
|
||||
else if ( ( *itemIt )->type() == QgsComposerItem::ComposerLabel )
|
||||
{
|
||||
QgsComposerLabel *label = qobject_cast< QgsComposerLabel *>( *itemIt );
|
||||
if ( !label )
|
||||
continue;
|
||||
|
||||
QString labelId = label->id().toUpper();
|
||||
if ( !mParameters.contains( labelId ) )
|
||||
continue;
|
||||
|
||||
QString labelParam = mParameters[ labelId ];
|
||||
if ( labelParam.isEmpty() )
|
||||
{
|
||||
//remove exported labels referenced in the request
|
||||
//but with empty string
|
||||
c->removeItem( label );
|
||||
delete label;
|
||||
continue;
|
||||
}
|
||||
|
||||
label->setText( labelParam );
|
||||
continue;
|
||||
}
|
||||
// forthly the composer HTMLs
|
||||
// an html item will be a composer frame and if it is we can try to get
|
||||
// its multiframe parent and then try to cast that to a composer html
|
||||
else if ( ( *itemIt )->type() == QgsComposerItem::ComposerFrame )
|
||||
{
|
||||
QgsComposerFrame *frame = qobject_cast< QgsComposerFrame *>( *itemIt );
|
||||
if ( !frame )
|
||||
continue;
|
||||
const QgsComposerMultiFrame *multiFrame = frame->multiFrame();
|
||||
const QgsComposerHtml *composerHtml = qobject_cast<const QgsComposerHtml *>( multiFrame );
|
||||
if ( !composerHtml )
|
||||
continue;
|
||||
|
||||
QgsComposerHtml *html = const_cast<QgsComposerHtml *>( composerHtml );
|
||||
QgsComposerFrame *htmlFrame = html->frame( 0 );
|
||||
QString htmlId = htmlFrame->id().toUpper();
|
||||
if ( !mParameters.contains( htmlId ) )
|
||||
{
|
||||
html->update();
|
||||
continue;
|
||||
}
|
||||
|
||||
QString url = mParameters[ htmlId ];
|
||||
//remove exported Htmls referenced in the request
|
||||
//but with empty string
|
||||
if ( url.isEmpty() )
|
||||
{
|
||||
c->removeMultiFrame( html );
|
||||
delete composerHtml;
|
||||
continue;
|
||||
}
|
||||
|
||||
QUrl newUrl( url );
|
||||
html->setUrl( newUrl );
|
||||
html->update();
|
||||
continue;
|
||||
map->grid()->setIntervalX( cMapParams.mGridX );
|
||||
map->grid()->setIntervalY( cMapParams.mGridY );
|
||||
}
|
||||
}
|
||||
|
||||
//update legend
|
||||
// if it has an auto-update model
|
||||
Q_FOREACH ( QgsComposerLegend *currentLegend, composerLegends )
|
||||
// Labels
|
||||
QList<QgsLayoutItemLabel *> labels;
|
||||
c->layoutItems<QgsLayoutItemLabel>( labels );
|
||||
for ( auto &label : labels )
|
||||
{
|
||||
if ( !currentLegend )
|
||||
QString labelId = label->id().toUpper();
|
||||
if ( !mParameters.contains( labelId ) )
|
||||
continue;
|
||||
|
||||
QString labelParam = mParameters[ labelId ];
|
||||
if ( labelParam.isEmpty() )
|
||||
{
|
||||
//remove exported labels referenced in the request
|
||||
//but with empty string
|
||||
c->removeItem( label );
|
||||
delete label;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( currentLegend->autoUpdateModel() )
|
||||
label->setText( labelParam );
|
||||
}
|
||||
|
||||
// HTMLs
|
||||
QList<QgsLayoutItemHtml *> htmls;
|
||||
c->layoutObjects<QgsLayoutItemHtml>( htmls );
|
||||
for ( auto &html : htmls )
|
||||
{
|
||||
if ( html->frameCount() == 0 )
|
||||
continue;
|
||||
QgsLayoutFrame *htmlFrame = html->frame( 0 );
|
||||
QString htmlId = htmlFrame->id().toUpper();
|
||||
if ( !mParameters.contains( htmlId ) )
|
||||
{
|
||||
html->update();
|
||||
continue;
|
||||
}
|
||||
|
||||
QString url = mParameters[ htmlId ];
|
||||
//remove exported Htmls referenced in the request
|
||||
//but with empty string
|
||||
if ( url.isEmpty() )
|
||||
{
|
||||
c->removeMultiFrame( html );
|
||||
delete html;
|
||||
continue;
|
||||
}
|
||||
|
||||
QUrl newUrl( url );
|
||||
html->setUrl( newUrl );
|
||||
html->update();
|
||||
}
|
||||
|
||||
|
||||
// legends
|
||||
QList<QgsLayoutItemLegend *> legends;
|
||||
c->layoutItems<QgsLayoutItemLegend>( legends );
|
||||
for ( auto &legend : legends )
|
||||
{
|
||||
if ( legend->autoUpdateModel() )
|
||||
{
|
||||
// the legend has an auto-update model
|
||||
// we will update it with map's layers
|
||||
const QgsComposerMap *map = currentLegend->composerMap();
|
||||
const QgsLayoutItemMap *map = legend->linkedMap();
|
||||
if ( !map )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
currentLegend->setAutoUpdateModel( false );
|
||||
legend->setAutoUpdateModel( false );
|
||||
|
||||
// get model and layer tree root of the legend
|
||||
QgsLegendModel *model = currentLegend->model();
|
||||
QgsLegendModel *model = legend->model();
|
||||
QStringList layerSet;
|
||||
Q_FOREACH ( QgsMapLayer *layer, map->layers() )
|
||||
for ( const auto &layer : map->layers() )
|
||||
layerSet << layer->id();
|
||||
|
||||
//setLayerIdsToLegendModel( model, layerSet, map->scale() );
|
||||
|
||||
// get model and layer tree root of the legend
|
||||
@ -640,9 +628,9 @@ namespace QgsWms
|
||||
// get layerIds find in the layer tree root
|
||||
QStringList layerIds = root->findLayerIds();
|
||||
|
||||
// Q_FOREACH layer find in the layer tree
|
||||
// find the layer in the layer tree
|
||||
// remove it if the layer id is not in map layerIds
|
||||
Q_FOREACH ( const QString &layerId, layerIds )
|
||||
for ( const auto &layerId : layerIds )
|
||||
{
|
||||
QgsLayerTreeLayer *nodeLayer = root->findLayer( layerId );
|
||||
if ( !nodeLayer )
|
||||
@ -668,24 +656,6 @@ namespace QgsWms
|
||||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
|
||||
QImage *QgsRenderer::getMap( HitTest *hitTest )
|
||||
{
|
||||
QgsMapSettings mapSettings;
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
class QgsCapabilitiesCache;
|
||||
class QgsCoordinateReferenceSystem;
|
||||
class QgsComposition;
|
||||
class QgsPrintLayout;
|
||||
class QgsConfigParser;
|
||||
class QgsFeature;
|
||||
class QgsFeatureRenderer;
|
||||
@ -274,8 +274,8 @@ namespace QgsWms
|
||||
//! Gets layer search rectangle (depending on request parameter, layer type, map and layer crs)
|
||||
QgsRectangle featureInfoSearchRect( QgsVectorLayer *ml, const QgsMapSettings &ms, const QgsRenderContext &rct, const QgsPointXY &infoPoint ) const;
|
||||
|
||||
//! configure the composition for the GetPrint request
|
||||
bool configureComposition( QgsComposition *c, const QgsMapSettings &mapSettings );
|
||||
//! configure the print layout for the GetPrint request
|
||||
bool configurePrintLayout( QgsPrintLayout *c, const QgsMapSettings &mapSettings );
|
||||
|
||||
//! Creates external WMS layer. Caller takes ownership
|
||||
QgsMapLayer *createExternalWMSLayer( const QString &externalLayerId ) const;
|
||||
|
@ -10,6 +10,11 @@ A XYZ map service is also available for multithreading testing:
|
||||
|
||||
?MAP=/path/to/projects.qgs&SERVICE=XYZ&X=1&Y=0&Z=1&LAYERS=world
|
||||
|
||||
Note that multi threading in QGIS server is not officially supported and
|
||||
it is not supposed to work in any case
|
||||
|
||||
Set MULTITHREADING environment varialbe to 1 to activate.
|
||||
|
||||
|
||||
For testing purposes, HTTP Basic can be enabled by setting the following
|
||||
environment variables:
|
||||
@ -188,7 +193,10 @@ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = ThreadedHTTPServer((QGIS_SERVER_HOST, QGIS_SERVER_PORT), Handler)
|
||||
if os.environ.get('MULTITHREADING') == '1':
|
||||
server = ThreadedHTTPServer((QGIS_SERVER_HOST, QGIS_SERVER_PORT), Handler)
|
||||
else:
|
||||
server = HTTPServer((QGIS_SERVER_HOST, QGIS_SERVER_PORT), Handler)
|
||||
if https:
|
||||
server.socket = ssl.wrap_socket(server.socket,
|
||||
certfile=QGIS_SERVER_PKI_CERTIFICATE,
|
||||
|
@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsServer.
|
||||
|
||||
Set the env var NO_ENCODED_OUTPUT to disable printing the base64 encoded image diff
|
||||
|
||||
FIXME: keep here only generic server tests and move specific services
|
||||
tests to test_qgsserver_<service>.py
|
||||
|
||||
@ -189,17 +191,23 @@ class QgsServerTestBase(unittest.TestCase):
|
||||
|
||||
with open(os.path.join(tempfile.gettempdir(), image + "_result.png"), "rb") as rendered_file:
|
||||
encoded_rendered_file = base64.b64encode(rendered_file.read())
|
||||
message = "Image is wrong\n%s\nImage:\necho '%s' | base64 -d >%s/%s_result.png" % (
|
||||
report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image
|
||||
)
|
||||
if os.environ.get('NO_ENCODED_OUTPUT'):
|
||||
message = "Image is wrong\: rendered file %s/%s_result.png" % (tempfile.gettempdir(), image)
|
||||
else:
|
||||
message = "Image is wrong\n%s\nImage:\necho '%s' | base64 -d >%s/%s_result.png" % (
|
||||
report, encoded_rendered_file.strip().decode('utf8'), tempfile.gettempdir(), image
|
||||
)
|
||||
|
||||
# If the failure is in image sizes the diff file will not exists.
|
||||
if os.path.exists(os.path.join(tempfile.gettempdir(), image + "_result_diff.png")):
|
||||
with open(os.path.join(tempfile.gettempdir(), image + "_result_diff.png"), "rb") as diff_file:
|
||||
encoded_diff_file = base64.b64encode(diff_file.read())
|
||||
message += "\nDiff:\necho '%s' | base64 -d > %s/%s_result_diff.png" % (
|
||||
encoded_diff_file.strip().decode('utf8'), tempfile.gettempdir(), image
|
||||
)
|
||||
if os.environ.get('NO_ENCODED_OUTPUT'):
|
||||
message = "Image is wrong\: diff file %s/%s_result_diff.png" % (tempfile.gettempdir(), image)
|
||||
else:
|
||||
encoded_diff_file = base64.b64encode(diff_file.read())
|
||||
message += "\nDiff:\necho '%s' | base64 -d > %s/%s_result_diff.png" % (
|
||||
encoded_diff_file.strip().decode('utf8'), tempfile.gettempdir(), image
|
||||
)
|
||||
|
||||
self.assertTrue(test, message)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""QGIS Unit tests for QgsServer WMS.
|
||||
"""QGIS Unit tests for QgsServer WMS GetPrint.
|
||||
|
||||
From build dir, run: ctest -R PyQgsServerWMS -V
|
||||
From build dir, run: ctest -R PyQgsServerWMSGetPrint -V
|
||||
|
||||
|
||||
.. note:: This program is free software; you can redistribute it and/or modify
|
||||
|
Before Width: | Height: | Size: 979 KiB After Width: | Height: | Size: 979 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 979 KiB |
Before Width: | Height: | Size: 293 KiB After Width: | Height: | Size: 296 KiB |
Before Width: | Height: | Size: 332 KiB After Width: | Height: | Size: 335 KiB |
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 264 KiB |
Before Width: | Height: | Size: 286 KiB After Width: | Height: | Size: 292 KiB |
Before Width: | Height: | Size: 272 KiB After Width: | Height: | Size: 306 KiB |
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 304 KiB |
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 296 KiB |
Before Width: | Height: | Size: 362 KiB After Width: | Height: | Size: 365 KiB |
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 256 KiB |
Before Width: | Height: | Size: 232 KiB After Width: | Height: | Size: 235 KiB |
Before Width: | Height: | Size: 293 KiB After Width: | Height: | Size: 296 KiB |
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 318 KiB |
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 252 KiB |