Skip to content

Commit 6be8d06

Browse files
committed
Fix searching for symbols by tag (fix #14445), add tests
1 parent e404a1c commit 6be8d06

4 files changed

Lines changed: 203 additions & 8 deletions

File tree

python/core/symbology-ng/qgsstylev2.sip

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@ class QgsStyleV2 : QObject
5959
*/
6060
int addTag( const QString& tagName );
6161

62+
/** Returns a list of all tags in the style database
63+
* @note added in QGIS 2.16
64+
* @see addTag()
65+
*/
66+
QStringList tags() const;
67+
6268
//! return a map of groupid and names for the given parent group
6369
QMap<int, QString> childGroupNames( const QString& parent = "" );
6470

src/core/symbology-ng/qgsstylev2.cpp

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,7 @@ bool QgsStyleV2::addSymbol( const QString& name, QgsSymbolV2* symbol, bool updat
104104

105105
bool QgsStyleV2::saveSymbol( const QString& name, QgsSymbolV2* symbol, int groupid, const QStringList& tags )
106106
{
107-
// TODO add support for tags and groups
108-
Q_UNUSED( tags );
109-
107+
// TODO add support for groups
110108
QDomDocument doc( "dummy" );
111109
QDomElement symEl = QgsSymbolLayerV2Utils::saveSymbol( name, symbol, doc );
112110
if ( symEl.isNull() )
@@ -128,6 +126,8 @@ bool QgsStyleV2::saveSymbol( const QString& name, QgsSymbolV2* symbol, int group
128126
return false;
129127
}
130128

129+
tagSymbol( SymbolEntity, name, tags );
130+
131131
emit symbolSaved( name, symbol );
132132

133133
return true;
@@ -209,9 +209,6 @@ bool QgsStyleV2::addColorRamp( const QString& name, QgsVectorColorRampV2* colorR
209209

210210
bool QgsStyleV2::saveColorRamp( const QString& name, QgsVectorColorRampV2* ramp, int groupid, const QStringList& tags )
211211
{
212-
// TODO add support for groups and tags
213-
Q_UNUSED( tags );
214-
215212
// insert it into the database
216213
QDomDocument doc( "dummy" );
217214
QDomElement rampEl = QgsSymbolLayerV2Utils::saveColorRamp( name, ramp, doc );
@@ -234,6 +231,8 @@ bool QgsStyleV2::saveColorRamp( const QString& name, QgsVectorColorRampV2* ramp,
234231
return false;
235232
}
236233

234+
tagSymbol( ColorrampEntity, name, tags );
235+
237236
return true;
238237
}
239238

@@ -654,6 +653,27 @@ int QgsStyleV2::addTag( const QString& tagname )
654653
return static_cast< int >( sqlite3_last_insert_rowid( mCurrentDB ) );
655654
}
656655

656+
QStringList QgsStyleV2::tags() const
657+
{
658+
if ( !mCurrentDB )
659+
return QStringList();
660+
661+
sqlite3_stmt *ppStmt;
662+
663+
char *query = sqlite3_mprintf( "SELECT name FROM tag" );
664+
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
665+
666+
QStringList tagList;
667+
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
668+
{
669+
tagList << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
670+
}
671+
672+
sqlite3_finalize( ppStmt );
673+
674+
return tagList;
675+
}
676+
657677
void QgsStyleV2::rename( StyleEntity type, int id, const QString& newName )
658678
{
659679
char *query;
@@ -781,22 +801,70 @@ QStringList QgsStyleV2::findSymbols( StyleEntity type, const QString& qword )
781801
return QStringList();
782802
}
783803

804+
// first find symbols with matching name
784805
QString item = ( type == SymbolEntity ) ? "symbol" : "colorramp";
785806
char *query = sqlite3_mprintf( "SELECT name FROM %q WHERE name LIKE '%%%q%%'",
786807
item.toUtf8().constData(), qword.toUtf8().constData() );
787808

788809
sqlite3_stmt *ppStmt;
789810
int nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );
790811

791-
QStringList symbols;
812+
QSet< QString > symbols;
792813
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
793814
{
794815
symbols << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
795816
}
796817

797818
sqlite3_finalize( ppStmt );
798819

799-
return symbols;
820+
// next add symbols with matching tags
821+
query = sqlite3_mprintf( "SELECT id FROM tag WHERE name LIKE '%%%q%%'", qword.toUtf8().constData() );
822+
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );
823+
824+
QStringList tagids;
825+
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
826+
{
827+
tagids << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
828+
}
829+
830+
sqlite3_finalize( ppStmt );
831+
832+
833+
QString dummy = tagids.join( ", " );
834+
835+
if ( type == SymbolEntity )
836+
{
837+
query = sqlite3_mprintf( "SELECT symbol_id FROM tagmap WHERE tag_id IN (%q)",
838+
dummy.toUtf8().constData() );
839+
}
840+
else
841+
{
842+
query = sqlite3_mprintf( "SELECT colorramp_id FROM ctagmap WHERE tag_id IN (%q)",
843+
dummy.toUtf8().constData() );
844+
}
845+
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );
846+
847+
QStringList symbolids;
848+
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
849+
{
850+
symbolids << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
851+
}
852+
853+
sqlite3_finalize( ppStmt );
854+
855+
856+
dummy = symbolids.join( ", " );
857+
query = sqlite3_mprintf( "SELECT name FROM %q WHERE id IN (%q)",
858+
item.toUtf8().constData(), dummy.toUtf8().constData() );
859+
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );
860+
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
861+
{
862+
symbols << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
863+
}
864+
865+
sqlite3_finalize( ppStmt );
866+
867+
return symbols.toList();
800868
}
801869

802870
bool QgsStyleV2::tagSymbol( StyleEntity type, const QString& symbol, const QStringList& tags )
@@ -870,6 +938,11 @@ bool QgsStyleV2::detagSymbol( StyleEntity type, const QString& symbol, const QSt
870938
{
871939
symbolid = sqlite3_column_int( ppStmt, 0 );
872940
}
941+
else
942+
{
943+
sqlite3_finalize( ppStmt );
944+
return false;
945+
}
873946

874947
sqlite3_finalize( ppStmt );
875948

src/core/symbology-ng/qgsstylev2.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@ class CORE_EXPORT QgsStyleV2 : public QObject
122122
*/
123123
int addTag( const QString& tagName );
124124

125+
/** Returns a list of all tags in the style database
126+
* @note added in QGIS 2.16
127+
* @see addTag()
128+
*/
129+
QStringList tags() const;
130+
125131
//! return a map of groupid and names for the given parent group
126132
QgsSymbolGroupMap childGroupNames( const QString& parent = "" );
127133

tests/src/core/testqgsstylev2.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class TestStyleV2 : public QObject
6565
void testCreateColorRamps();
6666
void testLoadColorRamps();
6767
void testSaveLoad();
68+
void testTags();
6869

6970
};
7071

@@ -255,5 +256,114 @@ void TestStyleV2::testSaveLoad()
255256
testLoadColorRamps();
256257
}
257258

259+
void TestStyleV2::testTags()
260+
{
261+
mStyle->clear();
262+
//add some tags
263+
int id = mStyle->addTag( "red" );
264+
QCOMPARE( id, mStyle->tagId( "red" ) );
265+
id = mStyle->addTag( "starry" );
266+
QCOMPARE( id, mStyle->tagId( "starry" ) );
267+
id = mStyle->addTag( "circle" );
268+
QCOMPARE( id, mStyle->tagId( "circle" ) );
269+
id = mStyle->addTag( "blue" );
270+
QCOMPARE( id, mStyle->tagId( "blue" ) );
271+
id = mStyle->addTag( "purple" );
272+
QCOMPARE( id, mStyle->tagId( "purple" ) );
273+
274+
QStringList tags = mStyle->tags();
275+
QCOMPARE( tags.count(), 5 );
276+
QVERIFY( tags.contains( "red" ) );
277+
QVERIFY( tags.contains( "starry" ) );
278+
QVERIFY( tags.contains( "circle" ) );
279+
QVERIFY( tags.contains( "blue" ) );
280+
QVERIFY( tags.contains( "purple" ) );
281+
282+
//remove tag
283+
mStyle->remove( QgsStyleV2::TagEntity, mStyle->tagId( "purple" ) );
284+
mStyle->remove( QgsStyleV2::TagEntity, -999 ); //bad id
285+
tags = mStyle->tags();
286+
QCOMPARE( tags.count(), 4 );
287+
QVERIFY( !tags.contains( "purple" ) );
288+
289+
//add some symbols
290+
QVERIFY( mStyle->saveSymbol( "symbol1", QgsMarkerSymbolV2::createSimple( QgsStringMap() ), 0, QStringList() << "red" << "starry" ) );
291+
mStyle->addSymbol( "blue starry", QgsMarkerSymbolV2::createSimple( QgsStringMap() ), true );
292+
mStyle->addSymbol( "red circle", QgsMarkerSymbolV2::createSimple( QgsStringMap() ), true );
293+
294+
//tag them
295+
QVERIFY( mStyle->tagSymbol( QgsStyleV2::SymbolEntity, "blue starry", QStringList() << "blue" << "starry" ) );
296+
QVERIFY( mStyle->tagSymbol( QgsStyleV2::SymbolEntity, "red circle", QStringList() << "red" << "circle" ) );
297+
//bad symbol name
298+
QVERIFY( !mStyle->tagSymbol( QgsStyleV2::SymbolEntity, "no symbol", QStringList() << "red" << "circle" ) );
299+
//tag which hasn't been added yet
300+
QVERIFY( mStyle->tagSymbol( QgsStyleV2::SymbolEntity, "red circle", QStringList() << "round" ) );
301+
tags = mStyle->tags();
302+
QVERIFY( tags.contains( "round" ) );
303+
304+
//check that tags have been applied
305+
tags = mStyle->tagsOfSymbol( QgsStyleV2::SymbolEntity, "blue starry" );
306+
QCOMPARE( tags.count(), 2 );
307+
QVERIFY( tags.contains( "blue" ) );
308+
QVERIFY( tags.contains( "starry" ) );
309+
tags = mStyle->tagsOfSymbol( QgsStyleV2::SymbolEntity, "red circle" );
310+
QCOMPARE( tags.count(), 3 );
311+
QVERIFY( tags.contains( "red" ) );
312+
QVERIFY( tags.contains( "circle" ) );
313+
QVERIFY( tags.contains( "round" ) );
314+
tags = mStyle->tagsOfSymbol( QgsStyleV2::SymbolEntity, "symbol1" );
315+
QCOMPARE( tags.count(), 2 );
316+
QVERIFY( tags.contains( "red" ) );
317+
QVERIFY( tags.contains( "starry" ) );
318+
319+
//remove a tag, including a non-present tag
320+
QVERIFY( mStyle->detagSymbol( QgsStyleV2::SymbolEntity, "blue starry", QStringList() << "bad" << "blue" ) );
321+
tags = mStyle->tagsOfSymbol( QgsStyleV2::SymbolEntity, "blue starry" );
322+
QCOMPARE( tags.count(), 1 );
323+
QVERIFY( tags.contains( "starry" ) );
324+
325+
//try to remove tag from non-existing symbol
326+
QVERIFY( !mStyle->detagSymbol( QgsStyleV2::SymbolEntity, "no symbol!", QStringList() << "bad" << "blue" ) );
327+
328+
//check symbols with tag
329+
QStringList symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "red" ) );
330+
QCOMPARE( symbols.count(), 2 );
331+
QVERIFY( symbols.contains( "symbol1" ) );
332+
QVERIFY( symbols.contains( "red circle" ) );
333+
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "starry" ) );
334+
QCOMPARE( symbols.count(), 2 );
335+
QVERIFY( symbols.contains( "symbol1" ) );
336+
QVERIFY( symbols.contains( "blue starry" ) );
337+
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "circle" ) );
338+
QCOMPARE( symbols.count(), 1 );
339+
QVERIFY( symbols.contains( "red circle" ) );
340+
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "round" ) );
341+
QCOMPARE( symbols.count(), 1 );
342+
QVERIFY( symbols.contains( "red circle" ) );
343+
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "blue" ) );
344+
QVERIFY( symbols.isEmpty() );
345+
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "no tag" ) );
346+
QVERIFY( symbols.isEmpty() );
347+
348+
//searching returns symbols with matching tags
349+
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "red" );
350+
QCOMPARE( symbols.count(), 2 );
351+
QVERIFY( symbols.contains( "symbol1" ) );
352+
QVERIFY( symbols.contains( "red circle" ) );
353+
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "symbol1" );
354+
QCOMPARE( symbols.count(), 1 );
355+
QVERIFY( symbols.contains( "symbol1" ) );
356+
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "starry" );
357+
QCOMPARE( symbols.count(), 2 );
358+
QVERIFY( symbols.contains( "symbol1" ) );
359+
QVERIFY( symbols.contains( "blue starry" ) );
360+
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "blue" );
361+
QCOMPARE( symbols.count(), 1 );
362+
QVERIFY( symbols.contains( "blue starry" ) );
363+
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "round" );
364+
QCOMPARE( symbols.count(), 1 );
365+
QVERIFY( symbols.contains( "red circle" ) );
366+
}
367+
258368
QTEST_MAIN( TestStyleV2 )
259369
#include "testqgsstylev2.moc"

0 commit comments

Comments
 (0)