1
/***************************************************************************
2
qgslegendmodel.cpp - description
5
copyright : (C) 2008 by Marco Hugentobler
6
email : marco dot hugentobler at karto dot baug dot ethz dot ch
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
***************************************************************************/
18
#include "qgslegendmodel.h"
20
#include "qgsmaplayer.h"
21
#include "qgsmaplayerregistry.h"
22
#include "qgsrasterlayer.h"
23
#include "qgsrenderer.h"
24
#include "qgssymbol.h"
25
#include "qgsvectordataprovider.h"
26
#include "qgsvectorlayer.h"
27
#include <QDomDocument>
28
#include <QDomElement>
31
QgsLegendModel::QgsLegendModel(): QStandardItemModel()
33
if ( QgsMapLayerRegistry::instance() )
35
connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( removeLayer( const QString& ) ) );
36
connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( addLayer( QgsMapLayer* ) ) );
40
QgsLegendModel::~QgsLegendModel()
45
void QgsLegendModel::setLayerSet( const QStringList& layerIds )
49
//for now clear the model and add the new entries
52
QStringList::const_iterator idIter = mLayerIds.constBegin();
53
QgsMapLayer* currentLayer = 0;
55
for ( ; idIter != mLayerIds.constEnd(); ++idIter )
57
currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *idIter );
60
QStandardItem* layerItem = new QStandardItem( currentLayer->name() );
61
//set layer id as user data into the item
62
layerItem->setData( QVariant( currentLayer->getLayerID() ) );
63
layerItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
65
invisibleRootItem()->setChild( invisibleRootItem()->rowCount(), layerItem );
67
switch ( currentLayer->type() )
69
case QgsMapLayer::VectorLayer:
70
addVectorLayerItems( layerItem, currentLayer );
72
case QgsMapLayer::RasterLayer:
73
addRasterLayerItem( layerItem, currentLayer );
82
int QgsLegendModel::addVectorLayerItems( QStandardItem* layerItem, QgsMapLayer* vlayer )
84
if ( !layerItem || !vlayer )
89
QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer *>( vlayer );
94
int opacity = vectorLayer->getTransparency();
96
const QgsRenderer* vectorRenderer = vectorLayer->renderer();
97
if ( !vectorRenderer )
102
//text field that describes classification attribute?
104
if ( settings.value( "/qgis/showLegendClassifiers", false ).toBool() )
106
QgsFieldMap layerFields = vectorLayer->pendingFields();
107
QgsAttributeList attributes = vectorRenderer->classificationAttributes();
108
QgsAttributeList::const_iterator att_it = attributes.constBegin();
109
for ( ; att_it != attributes.constEnd(); ++att_it )
111
QgsFieldMap::const_iterator fieldIt = layerFields.find( *att_it );
112
if ( fieldIt != layerFields.constEnd() )
114
QString attributeName = vectorLayer->attributeDisplayName( fieldIt.key() );
115
QStandardItem* attributeItem = new QStandardItem( attributeName );
116
layerItem->setChild( layerItem->rowCount(), 0, attributeItem );
121
const QList<QgsSymbol*> vectorSymbols = vectorRenderer->symbols();
122
QList<QgsSymbol*>::const_iterator symbolIt = vectorSymbols.constBegin();
124
for ( ; symbolIt != vectorSymbols.constEnd(); ++symbolIt )
126
if ( !( *symbolIt ) )
131
QStandardItem* currentSymbolItem = itemFromSymbol( *symbolIt, opacity );
132
if ( !currentSymbolItem )
137
layerItem->setChild( layerItem->rowCount(), 0, currentSymbolItem );
144
int QgsLegendModel::addRasterLayerItem( QStandardItem* layerItem, QgsMapLayer* rlayer )
146
if ( !layerItem || !rlayer )
151
QgsRasterLayer* rasterLayer = qobject_cast<QgsRasterLayer *>( rlayer );
157
QStandardItem* currentSymbolItem = new QStandardItem( QIcon( rasterLayer->legendAsPixmap( true ) ), "" );
159
int currentRowCount = layerItem->rowCount();
160
layerItem->setChild( currentRowCount, 0, currentSymbolItem );
165
void QgsLegendModel::insertSymbol( QgsSymbol* s )
167
QSet<QgsSymbol*>::iterator it = mSymbols.find( s );
168
if ( it != mSymbols.end() )
170
delete( *it ); //very unlikely
172
mSymbols.insert( s );
175
void QgsLegendModel::removeSymbol( QgsSymbol* s )
177
mSymbols.remove( s );
180
void QgsLegendModel::removeAllSymbols()
182
QSet<QgsSymbol*>::iterator it = mSymbols.begin();
183
for ( ; it != mSymbols.end(); ++it )
190
void QgsLegendModel::updateItem( QStandardItem* item )
197
//is it a toplevel layer item?
198
QModelIndex itemIndex = indexFromItem( item );
199
QModelIndex parentIndex = itemIndex.parent();
200
if ( !parentIndex.isValid() ) // a layer item?
205
//take QgsSymbol* from user data
206
QVariant symbolVariant = item->data();
207
QgsSymbol* symbol = 0;
208
if ( symbolVariant.canConvert<void*>() )
210
void* symbolData = symbolVariant.value<void*>();
211
symbol = ( QgsSymbol* )( symbolData );
214
if ( symbol ) //vector classification item
216
updateVectorClassificationItem( item, symbol, item->text() );
218
else if ( !item->icon().isNull() ) //raster classification item
220
updateRasterClassificationItem( item );
224
void QgsLegendModel::updateLayer( QStandardItem* layerItem )
231
QString layerId = layerItem->data().toString();
232
QgsMapLayer* mapLayer = QgsMapLayerRegistry::instance()->mapLayer( layerId );
235
//delete all the entries under layer item
236
int currentRowCount = layerItem->rowCount();
237
for ( int i = currentRowCount - 1; i >= 0; --i )
239
layerItem->removeRow( i );
242
//and add the new ones...
243
switch ( mapLayer->type() )
245
case QgsMapLayer::VectorLayer:
246
addVectorLayerItems( layerItem, mapLayer );
248
case QgsMapLayer::RasterLayer:
249
addRasterLayerItem( layerItem, mapLayer );
257
void QgsLegendModel::updateVectorClassificationItem( QStandardItem* classificationItem, QgsSymbol* symbol, QString itemText )
259
//this function uses the following logic to find a classification match:
260
//first test if there is a symbol where lowerbound - upperbound equels itemText
261
//if no match found, test if there is a symbol where label equals itemText
262
//still no match found. Test, if there is a symbol with same pen/brush/point symbol
265
QStandardItem* parentItem = classificationItem->parent();
271
//get maplayer object from parent item
272
QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( parentItem->data().toString() );
277
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
282
int opacity = vl->getTransparency();
284
const QgsRenderer* layerRenderer = vl->renderer();
285
if ( !layerRenderer )
290
QList<QgsSymbol*> symbolList = layerRenderer->symbols();
291
QList<QgsSymbol*>::iterator symbolIt;
292
QgsSymbol* currentSymbol = 0;
294
//try to find a symbol where lowerbound - upperbound matches item text
295
symbolIt = symbolList.begin();
296
for ( ; symbolIt != symbolList.end(); ++symbolIt )
298
currentSymbol = *symbolIt;
299
if ( currentSymbol->lowerValue() + " - " + currentSymbol->upperValue() == itemText )
301
removeSymbol( symbol );
302
parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity ) );
303
parentItem->removeRow( classificationItem->row() );
308
//try to find a symbol where lower value matches item text (non-numeric classifications)
309
symbolIt = symbolList.begin();
310
for ( ; symbolIt != symbolList.end(); ++symbolIt )
312
currentSymbol = *symbolIt;
313
if ( currentSymbol->lowerValue() == itemText )
315
removeSymbol( symbol );
316
parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity ) );
317
parentItem->removeRow( classificationItem->row() );
322
//try to find a symbol where label matches item text
323
symbolIt = symbolList.begin();
324
for ( ; symbolIt != symbolList.end(); ++symbolIt )
326
currentSymbol = *symbolIt;
327
if ( currentSymbol->label() == itemText )
329
removeSymbol( symbol );
330
parentItem->insertRow( classificationItem->row(), itemFromSymbol( currentSymbol, opacity ) );
331
parentItem->removeRow( classificationItem->row() );
338
void QgsLegendModel::updateRasterClassificationItem( QStandardItem* classificationItem )
340
if ( !classificationItem )
345
QStandardItem* parentItem = classificationItem->parent();
351
QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( parentItem->data().toString() );
357
QgsRasterLayer* rl = qobject_cast<QgsRasterLayer *>( ml );
363
QStandardItem* currentSymbolItem = new QStandardItem( QIcon( rl->legendAsPixmap( true ) ), "" );
364
parentItem->insertRow( 0, currentSymbolItem );
365
parentItem->removeRow( 1 );
368
void QgsLegendModel::removeLayer( const QString& layerId )
370
QStandardItem* currentLayerItem = 0;
372
int numRootItems = rowCount();
373
for ( int i = 0; i < numRootItems ; ++i )
375
currentLayerItem = item( i );
376
if ( !currentLayerItem )
381
QString currentId = currentLayerItem->data().toString();
382
if ( currentId == layerId )
384
removeRow( i ); //todo: also remove the subitems and their symbols...
385
emit layersChanged();
391
void QgsLegendModel::addLayer( QgsMapLayer* theMapLayer )
398
//append new layer item
399
QStandardItem* layerItem = new QStandardItem( theMapLayer->name() );
400
//set layer id as user data into the item
401
layerItem->setData( QVariant( theMapLayer->getLayerID() ) );
402
layerItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
403
invisibleRootItem()->setChild( invisibleRootItem()->rowCount(), layerItem );
405
//and child items of layer
406
switch ( theMapLayer->type() )
408
case QgsMapLayer::VectorLayer:
409
addVectorLayerItems( layerItem, theMapLayer );
411
case QgsMapLayer::RasterLayer:
412
addRasterLayerItem( layerItem, theMapLayer );
417
emit layersChanged();
420
QStandardItem* QgsLegendModel::itemFromSymbol( QgsSymbol* s, int opacity )
422
QStandardItem* currentSymbolItem = 0;
428
QString lowerValue = s->lowerValue();
429
QString upperValue = s->upperValue();
433
//Take the label as item text if it is there
434
if ( !label.isEmpty() )
439
else if ( lowerValue == upperValue || upperValue.isEmpty() )
441
itemText = lowerValue;
443
else //or value range
445
itemText = lowerValue + " - " + upperValue;
453
symbolImage = s->getPointSymbolAsImage();
456
symbolImage = s->getLineSymbolAsImage();
459
symbolImage = s->getPolygonSymbolAsImage();
465
if ( opacity != 255 )
467
//todo: manipulate image pixel by pixel...
469
for ( int i = 0; i < symbolImage.height(); ++i )
471
QRgb* scanLineBuffer = ( QRgb* ) symbolImage.scanLine( i );
472
for ( int j = 0; j < symbolImage.width(); ++j )
474
oldColor = symbolImage.pixel( j, i );
475
scanLineBuffer[j] = qRgba( qRed( oldColor ), qGreen( oldColor ), qBlue( oldColor ), opacity );
480
currentSymbolItem = new QStandardItem( QIcon( QPixmap::fromImage( symbolImage ) ), itemText );
482
if ( !currentSymbolItem )
487
//Pass deep copy of QgsSymbol as user data. Cast to void* necessary such that QMetaType handles it
488
QgsSymbol* symbolCopy = new QgsSymbol( *s );
489
currentSymbolItem->setData( QVariant::fromValue(( void* )symbolCopy ) );
490
insertSymbol( symbolCopy );
492
currentSymbolItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
493
return currentSymbolItem;
496
bool QgsLegendModel::writeXML( QDomElement& composerLegendElem, QDomDocument& doc ) const
498
if ( composerLegendElem.isNull() )
503
QDomElement legendModelElem = doc.createElement( "Model" );
505
//iterate over all items...
506
QStandardItem* currentLayerItem = 0;
507
QStandardItem* currentClassificationItem = 0;
508
int numRootItems = rowCount();
510
for ( int i = 0; i < numRootItems; ++i )
512
currentLayerItem = item( i );
513
QDomElement newLayerItem = doc.createElement( "LayerItem" );
514
newLayerItem.setAttribute( "layerId", currentLayerItem->data().toString() );
515
newLayerItem.setAttribute( "text", currentLayerItem->text() );
517
//add layer/classification items
518
int numClassItems = currentLayerItem->rowCount();
519
for ( int j = 0; j < numClassItems; ++j )
521
currentClassificationItem = currentLayerItem->child( j );
523
//store text and QgsSymbol for vector classification items
524
QVariant symbolVariant = currentClassificationItem->data();
525
QgsSymbol* symbol = 0;
526
if ( symbolVariant.canConvert<void*>() )
528
void* symbolData = symbolVariant.value<void*>();
529
symbol = ( QgsSymbol* )( symbolData );
533
QDomElement vectorClassElem = doc.createElement( "VectorClassificationItem" );
534
vectorClassElem.setAttribute( "text", currentClassificationItem->text() );
535
symbol->writeXML( vectorClassElem, doc, 0 );
536
newLayerItem.appendChild( vectorClassElem );
541
if ( currentClassificationItem->icon().isNull() )
543
QDomElement textItemElem = doc.createElement( "TextItem" );
544
textItemElem.setAttribute( "text", currentClassificationItem->text() );
545
newLayerItem.appendChild( textItemElem );
547
else //else it can only be a raster item
549
QDomElement rasterClassElem = doc.createElement( "RasterItem" );
550
rasterClassElem.setAttribute( "text", currentClassificationItem->text() );
551
//storing the layer id also in the raster item makes parsing easier
552
rasterClassElem.setAttribute( "layerId", currentLayerItem->data().toString() );
553
newLayerItem.appendChild( rasterClassElem );
557
legendModelElem.appendChild( newLayerItem );
560
composerLegendElem.appendChild( legendModelElem );
564
bool QgsLegendModel::readXML( const QDomElement& legendModelElem, const QDomDocument& doc )
566
if ( legendModelElem.isNull() )
571
//delete all stored symbols first
574
//iterate over layer items
575
QDomNodeList layerItemList = legendModelElem.elementsByTagName( "LayerItem" );
576
QgsMapLayer* currentLayer = 0; //store current layer to get
578
for ( int i = 0; i < layerItemList.size(); ++i )
580
QDomElement layerItemElem = layerItemList.at( i ).toElement();
581
QString layerId = layerItemElem.attribute( "layerId" );
583
QStandardItem* layerItem = new QStandardItem( layerItemElem.attribute( "text" ) );
585
//set layer id as user data into the item
586
layerItem->setData( QVariant( layerId ) );
587
layerItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable );
589
currentLayer = QgsMapLayerRegistry::instance()->mapLayer( layerId );
591
//go through all children of layerItemElem
592
QDomElement currentChildElement = layerItemElem.firstChildElement();
593
while ( !currentChildElement.isNull() )
595
QStandardItem* childItem = new QStandardItem( currentChildElement.attribute( "text" ) );
596
if ( currentChildElement.tagName() == "RasterItem" )
598
//get icon from current layer
599
QgsRasterLayer* rasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
602
childItem->setIcon( QIcon( rasterLayer->legendAsPixmap( true ) ) );
604
layerItem->setChild( layerItem->rowCount(), 0, childItem );
606
else if ( currentChildElement.tagName() == "VectorClassificationItem" )
608
//read QgsSymbol from xml and get icon
609
QgsVectorLayer* vectorLayer = qobject_cast<QgsVectorLayer *>( currentLayer );
613
QDomNodeList symbolNodeList = currentChildElement.elementsByTagName( "symbol" );
614
if ( symbolNodeList.size() > 0 )
616
QgsSymbol* symbol = new QgsSymbol( vectorLayer->geometryType() );
617
QDomNode symbolNode = symbolNodeList.at( 0 );
618
symbol->readXML( symbolNode, vectorLayer );
619
childItem->setData( QVariant::fromValue(( void* )symbol ) );
622
switch ( symbol->type() )
625
childItem->setIcon( QIcon( QPixmap::fromImage( symbol->getPointSymbolAsImage() ) ) );
628
childItem->setIcon( QIcon( QPixmap::fromImage( symbol->getLineSymbolAsImage() ) ) );
631
childItem->setIcon( QIcon( QPixmap::fromImage( symbol->getPolygonSymbolAsImage() ) ) );
633
case QGis::UnknownGeometry:
637
insertSymbol( symbol );
640
layerItem->setChild( layerItem->rowCount(), 0, childItem );
642
else if ( currentChildElement.tagName() == "TextItem" )
644
layerItem->setChild( layerItem->rowCount(), 0, childItem );
646
else //unknown tag name, don't add item
651
currentChildElement = currentChildElement.nextSiblingElement();
654
invisibleRootItem()->setChild( invisibleRootItem()->rowCount(), layerItem );