1
/***************************************************************************
5
copyright : (C) 2005 by Radim Blazek
7
***************************************************************************/
9
/***************************************************************************
11
* This program is free software; you can redistribute it and/or modify *
12
* it under the terms of the GNU General Public License as published by *
13
* the Free Software Foundation; either version 2 of the License, or *
14
* (at your option) any later version. *
16
***************************************************************************/
22
#include <qcombobox.h>
29
#include <qlineedit.h>
30
#include <qpointarray.h>
32
#include <qcheckbox.h>
35
#include "qgsproject.h"
37
#include "qgsmaptopixel.h"
38
#include "qgsmapcanvas.h"
39
#include "qgsmaplayer.h"
40
#include "qgsvectorlayer.h"
41
#include "qgscomposition.h"
42
#include "qgscomposermap.h"
44
QgsComposerMap::QgsComposerMap ( QgsComposition *composition, int id, int x, int y, int width, int height )
45
: QCanvasRectangle(x,y,width,height,0)
47
mComposition = composition;
49
mMapCanvas = mComposition->mapCanvas();
50
mName = QString(tr("Map %1").arg(mId));
56
setCanvas(mComposition->canvas());
57
QCanvasRectangle::show();
62
QgsComposerMap::QgsComposerMap ( QgsComposition *composition, int id )
63
: QCanvasRectangle(0,0,10,10,0)
65
mComposition = composition;
67
mMapCanvas = mComposition->mapCanvas();
68
mName = QString(tr("Map %1").arg(mId));
75
setCanvas(mComposition->canvas());
76
QCanvasRectangle::show();
79
void QgsComposerMap::init ()
83
mUserExtent = mMapCanvas->extent();
87
mCacheUpdated = false;
90
mCalculateComboBox->insertItem( tr("Extent (calculate scale)"), Scale );
91
mCalculateComboBox->insertItem( tr("Scale (calculate extent)"), Extent );
94
setPlotStyle ( QgsComposition::Preview );
98
mPreviewModeComboBox->insertItem ( "Cache", Cache );
99
mPreviewModeComboBox->insertItem ( "Render", Render );
100
mPreviewModeComboBox->insertItem ( "Rectangle", Rectangle );
101
mPreviewModeComboBox->setCurrentItem ( Cache );
103
mWidthScale = 1.0 / mComposition->scale();
109
QCanvasRectangle::setZ(20);
112
connect ( mMapCanvas, SIGNAL(addedLayer(QgsMapLayer *)), this, SLOT(mapCanvasChanged()) );
113
connect ( mMapCanvas, SIGNAL(removedLayer(QString)), this, SLOT(mapCanvasChanged()) );
114
connect ( mMapCanvas, SIGNAL(removedAll()), this, SLOT(mapCanvasChanged()) );
117
QgsComposerMap::~QgsComposerMap()
119
std::cerr << "QgsComposerMap::~QgsComposerMap" << std::endl;
122
void QgsComposerMap::draw ( QPainter *painter, QgsRect *extent, QgsMapToPixel *transform, QPaintDevice *device )
124
mMapCanvas->freeze(true); // necessary ?
125
int nlayers = mMapCanvas->layerCount();
127
for ( int i = 0; i < nlayers; i++ ) {
128
QgsMapLayer *layer = mMapCanvas->getZpos(i);
130
if ( !layer->visible() ) continue;
132
if ( layer->type() == QgsMapLayer::VECTOR ) {
133
QgsVectorLayer *vector = dynamic_cast <QgsVectorLayer*> (layer);
135
double widthScale = mWidthScale * mComposition->scale();
136
if ( plotStyle() == QgsComposition::Preview && mPreviewMode == Render ) {
137
widthScale *= mComposition->viewScale();
139
double symbolScale = mSymbolScale * mComposition->scale();
140
vector->draw( painter, extent, transform, device, widthScale, symbolScale, 0 );
144
if ( plotStyle() == QgsComposition::Print || plotStyle() == QgsComposition::Postscript ) {
145
// we have to rescale the raster to get requested resolution
147
// calculate relation between composition point size and requested resolution (in mm)
148
double multip = (1. / mComposition->scale()) / (25.4 / mComposition->resolution()) ;
150
double sc = mExtent.width() / (multip*QCanvasRectangle::width());
152
QgsMapToPixel trans ( sc, multip*QCanvasRectangle::height(), mExtent.yMin(), mExtent.xMin() );
155
painter->scale( 1./multip, 1./multip);
157
layer->draw( painter, extent, &trans, device );
163
layer->draw( painter, extent, transform, device );
168
// Draw vector labels
169
for ( int i = 0; i < nlayers; i++ ) {
170
QgsMapLayer *layer = mMapCanvas->getZpos(i);
172
if ( !layer->visible() ) continue;
174
if ( layer->type() == QgsMapLayer::VECTOR ) {
175
QgsVectorLayer *vector = dynamic_cast <QgsVectorLayer*> (layer);
177
if ( vector->labelOn() ) {
178
double fontScale = 25.4 * mFontScale * mComposition->scale() / 72;
179
if ( plotStyle() == QgsComposition::Postscript ) {
180
fontScale = QgsComposition::psFontScaleFactor() * 72.0 / mComposition->resolution();
182
vector->drawLabels ( painter, extent, transform, device, fontScale );
188
mMapCanvas->freeze(false);
191
void QgsComposerMap::setUserExtent ( QgsRect const & rect )
196
QCanvasRectangle::canvas()->setChanged( QCanvasRectangle::boundingRect() );
197
QCanvasRectangle::update();
198
QCanvasRectangle::canvas()->update();
201
void QgsComposerMap::cache ( void )
203
// Create preview on some reasonable size. It was slow with cca 1500x1500 points on 2x1.5GHz
204
// Note: The resolution should also respect the line widths, it means that
205
// 1 pixel in cache should have ia similar size as 1 pixel in canvas
206
// but it can result in big cache -> limit
208
int w = QCanvasRectangle::width() < 1000 ? QCanvasRectangle::width() : 1000;
209
int h = (int) ( mExtent.height() * w / mExtent.width() );
210
// It can happen that extent is not initialised well -> check
211
if ( h < 1 || h > 10000 ) h = w;
213
std::cout << "extent = " << mExtent.width() << " x " << mExtent.height() << std::endl;
214
std::cout << "cache = " << w << " x " << h << std::endl;
216
mCacheExtent = QgsRect ( mExtent );
217
double scale = mExtent.width() / w;
218
mCacheExtent.setXmax ( mCacheExtent.xMin() + w * scale );
219
mCacheExtent.setYmax ( mCacheExtent.yMin() + h * scale );
221
mCachePixmap.resize( w, h );
223
// WARNING: ymax in QgsMapToPixel is device height!!!
224
QgsMapToPixel transform(scale, h, mCacheExtent.yMin(), mCacheExtent.xMin() );
226
std::cout << "transform = " << transform.showParameters().local8Bit() << std::endl;
228
mCachePixmap.fill(QColor(255,255,255));
230
QPainter p(&mCachePixmap);
232
draw( &p, &mCacheExtent, &transform, &mCachePixmap );
235
mNumCachedLayers = mMapCanvas->layerCount();
236
mCacheUpdated = true;
239
void QgsComposerMap::draw ( QPainter & painter )
241
if ( mDrawing ) return;
244
std::cout << "draw mPlotStyle = " << plotStyle()
245
<< " mPreviewMode = " << mPreviewMode << std::endl;
247
if ( plotStyle() == QgsComposition::Preview && mPreviewMode == Cache ) { // Draw from cache
248
std::cout << "use cache" << std::endl;
250
if ( !mCacheUpdated || mMapCanvas->layerCount() != mNumCachedLayers ) {
254
// Scale so that the cache fills the map rectangle
255
double scale = 1.0 * QCanvasRectangle::width() / mCachePixmap.width();
260
painter.translate ( QCanvasRectangle::x(), QCanvasRectangle::y() );
261
painter.scale(scale,scale);
262
std::cout << "scale = " << scale << std::endl;
263
std::cout << "translate: " << QCanvasRectangle::x() << ", " << QCanvasRectangle::y() << std::endl;
264
// Note: drawing only a visible part of the pixmap doesn't make it much faster
265
painter.drawPixmap(0,0, mCachePixmap);
269
} else if ( (plotStyle() == QgsComposition::Preview && mPreviewMode == Render) ||
270
plotStyle() == QgsComposition::Print ||
271
plotStyle() == QgsComposition::Postscript )
273
std::cout << "render" << std::endl;
275
double scale = mExtent.width() / QCanvasRectangle::width();
276
QgsMapToPixel transform(scale, QCanvasRectangle::height(), mExtent.yMin(), mExtent.xMin() );
279
painter.translate ( QCanvasRectangle::x(), QCanvasRectangle::y() );
281
// Note: CoordDevice doesn't work well
282
painter.setClipRect ( 0, 0, QCanvasRectangle::width(), QCanvasRectangle::height(), QPainter::CoordPainter );
284
draw( &painter, &mExtent, &transform, painter.device() );
290
painter.setPen( QPen(QColor(0,0,0), 1) );
291
painter.setBrush( Qt::NoBrush );
293
painter.translate ( QCanvasRectangle::x(), QCanvasRectangle::y() );
294
painter.drawRect ( 0, 0, QCanvasRectangle::width()+1, QCanvasRectangle::height()+1 ); // is it right?
298
// Show selected / Highlight
299
if ( mSelected && plotStyle() == QgsComposition::Preview ) {
300
painter.setPen( mComposition->selectionPen() );
301
painter.setBrush( mComposition->selectionBrush() );
302
int x = (int) QCanvasRectangle::x();
303
int y = (int) QCanvasRectangle::y();
304
int s = mComposition->selectionBoxSize();
306
painter.drawRect ( x, y, s, s );
307
x += QCanvasRectangle::width();
308
painter.drawRect ( x-s, y, s, s );
309
y += QCanvasRectangle::height();
310
painter.drawRect ( x-s, y-s, s, s );
311
x -= QCanvasRectangle::width();
312
painter.drawRect ( x, y-s, s, s );
318
void QgsComposerMap::sizeChanged ( void )
321
w = mComposition->fromMM ( mWidthLineEdit->text().toDouble() );
322
h = mComposition->fromMM ( mHeightLineEdit->text().toDouble() );
324
QCanvasRectangle::setSize ( w, h);
327
QCanvasRectangle::canvas()->setChanged( QCanvasRectangle::boundingRect() );
328
QCanvasRectangle::update();
329
QCanvasRectangle::canvas()->update();
334
void QgsComposerMap::calculateChanged ( void )
336
mCalculate = mCalculateComboBox->currentItem();
338
if ( mCalculate == Scale ) { // return to extent defined by user
341
mCacheUpdated = false;
342
//QCanvasRectangle::canvas()->setAllChanged(); // must be setAllChanged(), not sure why
343
QCanvasRectangle::canvas()->setChanged( QCanvasRectangle::boundingRect() );
344
QCanvasRectangle::canvas()->update();
346
mComposition->emitMapChanged ( mId );
352
double QgsComposerMap::scaleFromUserScale ( double us )
356
switch ( QgsProject::instance()->mapUnits() ) {
358
s = 1000. * mComposition->scale() / us;
361
s = 304.8 * mComposition->scale() / us;
364
s = mComposition->scale() / us;
370
double QgsComposerMap::userScaleFromScale ( double s )
374
switch ( QgsProject::instance()->mapUnits() ) {
376
us = 1000. * mComposition->scale() / s;
379
us = 304.8 * mComposition->scale() / s;
382
us = mComposition->scale() / s;
389
void QgsComposerMap::mapScaleChanged ( void )
391
std::cout << "QgsComposerMap::mapScaleChanged" << std::endl;
393
mCalculate = mCalculateComboBox->currentItem();
395
mUserScale = mScaleLineEdit->text().toDouble();
397
mScale = scaleFromUserScale ( mUserScale );
401
mCacheUpdated = false;
402
QCanvasRectangle::canvas()->setChanged( QCanvasRectangle::boundingRect() );
403
QCanvasRectangle::update();
404
QCanvasRectangle::canvas()->update();
407
mComposition->emitMapChanged ( mId );
410
void QgsComposerMap::scaleChanged ( void )
412
mWidthScale = mWidthScaleLineEdit->text().toDouble();
413
mSymbolScale = mSymbolScaleLineEdit->text().toDouble();
414
mFontScale = mFontScaleLineEdit->text().toDouble();
416
mCacheUpdated = false;
417
QCanvasRectangle::canvas()->setChanged( QCanvasRectangle::boundingRect() );
418
QCanvasRectangle::update();
419
QCanvasRectangle::canvas()->update();
422
mComposition->emitMapChanged ( mId );
425
void QgsComposerMap::mapCanvasChanged ( void )
427
std::cout << "QgsComposerMap::canvasChanged" << std::endl;
429
mCacheUpdated = false;
430
QCanvasRectangle::canvas()->setChanged( QCanvasRectangle::boundingRect() );
433
void QgsComposerMap::previewModeChanged ( int i )
435
mPreviewMode = (PreviewMode) i;
439
void QgsComposerMap::recalculate ( void )
441
std::cout << "QgsComposerMap::recalculate mCalculate = " << mCalculate << std::endl;
443
if ( mCalculate == Scale )
445
// Calculate scale from extent and rectangle
446
double xscale = QCanvasRectangle::width() / mUserExtent.width();
447
double yscale = QCanvasRectangle::height() / mUserExtent.height();
449
mExtent = mUserExtent;
451
if ( xscale < yscale ) {
454
double d = ( 1. * QCanvasRectangle::height() / mScale - mUserExtent.height() ) / 2 ;
455
mExtent.setYmin ( mUserExtent.yMin() - d );
456
mExtent.setYmax ( mUserExtent.yMax() + d );
460
double d = ( 1.* QCanvasRectangle::width() / mScale - mUserExtent.width() ) / 2 ;
461
mExtent.setXmin ( mUserExtent.xMin() - d );
462
mExtent.setXmax ( mUserExtent.xMax() + d );
465
mUserScale = userScaleFromScale ( mScale );
470
double xc = ( mUserExtent.xMax() + mUserExtent.xMin() ) / 2;
471
double yc = ( mUserExtent.yMax() + mUserExtent.yMin() ) / 2;
473
double width = QCanvasRectangle::width() / mScale;
474
double height = QCanvasRectangle::height() / mScale;
476
mExtent.setXmin ( xc - width/2 );
477
mExtent.setXmax ( xc + width/2 );
478
mExtent.setYmin ( yc - height/2 );
479
mExtent.setYmax ( yc + height/2 );
483
std::cout << "mUserExtent = " << mUserExtent.stringRep().local8Bit() << std::endl;
484
std::cout << "mScale = " << mScale << std::endl;
485
std::cout << "mExtent = " << mExtent.stringRep().local8Bit() << std::endl;
488
mCacheUpdated = false;
491
void QgsComposerMap::frameChanged ( )
493
mFrame = mFrameCheckBox->isChecked();
495
QCanvasRectangle::canvas()->setChanged( QCanvasRectangle::boundingRect() );
496
QCanvasRectangle::update();
497
QCanvasRectangle::canvas()->update();
503
void QgsComposerMap::setOptions ( void )
505
std::cout << "QgsComposerMap::setOptions" << std::endl;
507
mNameLabel->setText ( mName );
509
mCalculateComboBox->setCurrentItem( mCalculate );
511
mWidthLineEdit->setText ( QString("%1").arg( mComposition->toMM(QCanvasRectangle::width()), 0,'g') );
512
mHeightLineEdit->setText ( QString("%1").arg( mComposition->toMM(QCanvasRectangle::height()),0,'g') );
515
switch ( QgsProject::instance()->mapUnits() ) {
518
mScaleLineEdit->setText ( QString("%1").arg((int)mUserScale) );
521
mScaleLineEdit->setText ( QString("%1").arg(mUserScale,0,'f') );
524
if ( mCalculate == Scale ) {
525
mScaleLineEdit->setEnabled(false);
527
mScaleLineEdit->setEnabled(true);
530
mWidthScaleLineEdit->setText ( QString("%1").arg(mWidthScale,0,'g',2) );
531
mSymbolScaleLineEdit->setText ( QString("%1").arg(mSymbolScale,0,'g',2) );
532
mFontScaleLineEdit->setText ( QString("%1").arg(mFontScale,0,'g',2) );
534
mFrameCheckBox->setChecked ( mFrame );
536
mPreviewModeComboBox->setCurrentItem( mPreviewMode );
539
void QgsComposerMap::setCurrentExtent ( void )
541
mUserExtent = mMapCanvas->extent();
543
QCanvasRectangle::canvas()->setChanged( QCanvasRectangle::boundingRect() );
544
QCanvasRectangle::update();
545
QCanvasRectangle::canvas()->update();
548
mComposition->emitMapChanged ( mId );
551
void QgsComposerMap::setSelected ( bool s )
554
QCanvasRectangle::update(); // show highlight
557
bool QgsComposerMap::selected( void )
562
void QgsComposerMap::setCacheUpdated ( bool u )
567
double QgsComposerMap::scale ( void ) { return mScale; }
569
QWidget *QgsComposerMap::options ( void )
572
return ( dynamic_cast <QWidget *> (this) );
575
QString QgsComposerMap::name ( void )
580
double QgsComposerMap::widthScale (void ) { return mWidthScale ; }
581
double QgsComposerMap::symbolScale (void ) { return mSymbolScale ; }
582
double QgsComposerMap::fontScale (void ) { return mFontScale ; }
584
bool QgsComposerMap::writeSettings ( void )
587
path.sprintf("/composition_%d/map_%d/", mComposition->id(), mId );
588
QgsProject::instance()->writeEntry( "Compositions", path+"x", mComposition->toMM((int)QCanvasRectangle::x()) );
589
QgsProject::instance()->writeEntry( "Compositions", path+"y", mComposition->toMM((int)QCanvasRectangle::y()) );
590
QgsProject::instance()->writeEntry( "Compositions", path+"width", mComposition->toMM(QCanvasRectangle::width()) );
591
QgsProject::instance()->writeEntry( "Compositions", path+"height", mComposition->toMM(QCanvasRectangle::height()) );
593
if ( mCalculate == Scale ) {
594
QgsProject::instance()->writeEntry( "Compositions", path+"calculate", QString("scale") );
596
QgsProject::instance()->writeEntry( "Compositions", path+"calculate", QString("extent") );
599
QgsProject::instance()->writeEntry( "Compositions", path+"north", mUserExtent.yMax() );
600
QgsProject::instance()->writeEntry( "Compositions", path+"south", mUserExtent.yMin() );
601
QgsProject::instance()->writeEntry( "Compositions", path+"east", mUserExtent.xMax() );
602
QgsProject::instance()->writeEntry( "Compositions", path+"west", mUserExtent.xMin() );
604
QgsProject::instance()->writeEntry( "Compositions", path+"scale", mUserScale );
606
QgsProject::instance()->writeEntry( "Compositions", path+"widthscale", mWidthScale );
607
QgsProject::instance()->writeEntry( "Compositions", path+"symbolscale", mSymbolScale );
608
QgsProject::instance()->writeEntry( "Compositions", path+"fontscale", mFontScale );
610
QgsProject::instance()->writeEntry( "Compositions", path+"frame", mFrame );
612
QgsProject::instance()->writeEntry( "Compositions", path+"previewmode", mPreviewMode );
617
bool QgsComposerMap::readSettings ( void )
621
path.sprintf("/composition_%d/map_%d/", mComposition->id(), mId );
624
QCanvasRectangle::setX( mComposition->fromMM(QgsProject::instance()->readDoubleEntry( "Compositions", path+"x", 0, &ok)) );
625
QCanvasRectangle::setY( mComposition->fromMM(QgsProject::instance()->readDoubleEntry( "Compositions", path+"y", 0, &ok)) );
626
int w = mComposition->fromMM(QgsProject::instance()->readDoubleEntry( "Compositions", path+"width", 100, &ok)) ;
627
int h = mComposition->fromMM(QgsProject::instance()->readDoubleEntry( "Compositions", path+"height", 100, &ok)) ;
628
QCanvasRectangle::setSize(w,h);
630
QString calculate = QgsProject::instance()->readEntry("Compositions", path+"calculate", "scale", &ok);
631
if ( calculate == "extent" ) {
637
mUserExtent.setYmax ( QgsProject::instance()->readDoubleEntry( "Compositions", path+"north", 100, &ok) );
638
mUserExtent.setYmin ( QgsProject::instance()->readDoubleEntry( "Compositions", path+"south", 0, &ok) );
639
mUserExtent.setXmax ( QgsProject::instance()->readDoubleEntry( "Compositions", path+"east", 100, &ok) );
640
mUserExtent.setXmin ( QgsProject::instance()->readDoubleEntry( "Compositions", path+"west", 0, &ok) );
642
mUserScale = QgsProject::instance()->readDoubleEntry( "Compositions", path+"scale", 1000., &ok);
643
mScale = scaleFromUserScale ( mUserScale );
645
mWidthScale = QgsProject::instance()->readDoubleEntry("Compositions", path+"widthscale", 1., &ok);
646
mSymbolScale = QgsProject::instance()->readDoubleEntry("Compositions", path+"symbolscale", 1., &ok);
647
mFontScale = QgsProject::instance()->readDoubleEntry("Compositions", path+"fontscale", 1., &ok);
649
mFrame = QgsProject::instance()->readBoolEntry("Compositions", path+"frame", true, &ok);
651
mPreviewMode = (PreviewMode) QgsProject::instance()->readNumEntry("Compositions", path+"previewmode", Cache, &ok);
658
bool QgsComposerMap::removeSettings ( void )
661
path.sprintf("/composition_%d/map_%d", mComposition->id(), mId );
662
return QgsProject::instance()->removeEntry ( "Compositions", path );
665
bool QgsComposerMap::writeXML( QDomNode & node, QDomDocument & document, bool temp )
670
bool QgsComposerMap::readXML( QDomNode & node )