Skip to content

Commit

Permalink
Fix searching for symbols by tag (fix #14445), add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Mar 9, 2016
1 parent e404a1c commit 6be8d06
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 8 deletions.
6 changes: 6 additions & 0 deletions python/core/symbology-ng/qgsstylev2.sip
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ class QgsStyleV2 : QObject
*/
int addTag( const QString& tagName );

/** Returns a list of all tags in the style database
* @note added in QGIS 2.16
* @see addTag()
*/
QStringList tags() const;

//! return a map of groupid and names for the given parent group
QMap<int, QString> childGroupNames( const QString& parent = "" );

Expand Down
89 changes: 81 additions & 8 deletions src/core/symbology-ng/qgsstylev2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,7 @@ bool QgsStyleV2::addSymbol( const QString& name, QgsSymbolV2* symbol, bool updat

bool QgsStyleV2::saveSymbol( const QString& name, QgsSymbolV2* symbol, int groupid, const QStringList& tags )
{
// TODO add support for tags and groups
Q_UNUSED( tags );

// TODO add support for groups
QDomDocument doc( "dummy" );
QDomElement symEl = QgsSymbolLayerV2Utils::saveSymbol( name, symbol, doc );
if ( symEl.isNull() )
Expand All @@ -128,6 +126,8 @@ bool QgsStyleV2::saveSymbol( const QString& name, QgsSymbolV2* symbol, int group
return false;
}

tagSymbol( SymbolEntity, name, tags );

emit symbolSaved( name, symbol );

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

bool QgsStyleV2::saveColorRamp( const QString& name, QgsVectorColorRampV2* ramp, int groupid, const QStringList& tags )
{
// TODO add support for groups and tags
Q_UNUSED( tags );

// insert it into the database
QDomDocument doc( "dummy" );
QDomElement rampEl = QgsSymbolLayerV2Utils::saveColorRamp( name, ramp, doc );
Expand All @@ -234,6 +231,8 @@ bool QgsStyleV2::saveColorRamp( const QString& name, QgsVectorColorRampV2* ramp,
return false;
}

tagSymbol( ColorrampEntity, name, tags );

return true;
}

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

QStringList QgsStyleV2::tags() const
{
if ( !mCurrentDB )
return QStringList();

sqlite3_stmt *ppStmt;

char *query = sqlite3_mprintf( "SELECT name FROM tag" );
int nError = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, nullptr );

QStringList tagList;
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
tagList << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
}

sqlite3_finalize( ppStmt );

return tagList;
}

void QgsStyleV2::rename( StyleEntity type, int id, const QString& newName )
{
char *query;
Expand Down Expand Up @@ -781,22 +801,70 @@ QStringList QgsStyleV2::findSymbols( StyleEntity type, const QString& qword )
return QStringList();
}

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

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

QStringList symbols;
QSet< QString > symbols;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbols << QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( ppStmt, 0 ) ) );
}

sqlite3_finalize( ppStmt );

return symbols;
// next add symbols with matching tags
query = sqlite3_mprintf( "SELECT id FROM tag WHERE name LIKE '%%%q%%'", qword.toUtf8().constData() );
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );

QStringList tagids;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
tagids << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
}

sqlite3_finalize( ppStmt );


QString dummy = tagids.join( ", " );

if ( type == SymbolEntity )
{
query = sqlite3_mprintf( "SELECT symbol_id FROM tagmap WHERE tag_id IN (%q)",
dummy.toUtf8().constData() );
}
else
{
query = sqlite3_mprintf( "SELECT colorramp_id FROM ctagmap WHERE tag_id IN (%q)",
dummy.toUtf8().constData() );
}
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );

QStringList symbolids;
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbolids << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
}

sqlite3_finalize( ppStmt );


dummy = symbolids.join( ", " );
query = sqlite3_mprintf( "SELECT name FROM %q WHERE id IN (%q)",
item.toUtf8().constData(), dummy.toUtf8().constData() );
nErr = sqlite3_prepare_v2( mCurrentDB, query, -1, &ppStmt, NULL );
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
symbols << QString::fromUtf8(( const char * ) sqlite3_column_text( ppStmt, 0 ) );
}

sqlite3_finalize( ppStmt );

return symbols.toList();
}

bool QgsStyleV2::tagSymbol( StyleEntity type, const QString& symbol, const QStringList& tags )
Expand Down Expand Up @@ -870,6 +938,11 @@ bool QgsStyleV2::detagSymbol( StyleEntity type, const QString& symbol, const QSt
{
symbolid = sqlite3_column_int( ppStmt, 0 );
}
else
{
sqlite3_finalize( ppStmt );
return false;
}

sqlite3_finalize( ppStmt );

Expand Down
6 changes: 6 additions & 0 deletions src/core/symbology-ng/qgsstylev2.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ class CORE_EXPORT QgsStyleV2 : public QObject
*/
int addTag( const QString& tagName );

/** Returns a list of all tags in the style database
* @note added in QGIS 2.16
* @see addTag()
*/
QStringList tags() const;

//! return a map of groupid and names for the given parent group
QgsSymbolGroupMap childGroupNames( const QString& parent = "" );

Expand Down
110 changes: 110 additions & 0 deletions tests/src/core/testqgsstylev2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class TestStyleV2 : public QObject
void testCreateColorRamps();
void testLoadColorRamps();
void testSaveLoad();
void testTags();

};

Expand Down Expand Up @@ -255,5 +256,114 @@ void TestStyleV2::testSaveLoad()
testLoadColorRamps();
}

void TestStyleV2::testTags()
{
mStyle->clear();
//add some tags
int id = mStyle->addTag( "red" );
QCOMPARE( id, mStyle->tagId( "red" ) );
id = mStyle->addTag( "starry" );
QCOMPARE( id, mStyle->tagId( "starry" ) );
id = mStyle->addTag( "circle" );
QCOMPARE( id, mStyle->tagId( "circle" ) );
id = mStyle->addTag( "blue" );
QCOMPARE( id, mStyle->tagId( "blue" ) );
id = mStyle->addTag( "purple" );
QCOMPARE( id, mStyle->tagId( "purple" ) );

QStringList tags = mStyle->tags();
QCOMPARE( tags.count(), 5 );
QVERIFY( tags.contains( "red" ) );
QVERIFY( tags.contains( "starry" ) );
QVERIFY( tags.contains( "circle" ) );
QVERIFY( tags.contains( "blue" ) );
QVERIFY( tags.contains( "purple" ) );

//remove tag
mStyle->remove( QgsStyleV2::TagEntity, mStyle->tagId( "purple" ) );
mStyle->remove( QgsStyleV2::TagEntity, -999 ); //bad id
tags = mStyle->tags();
QCOMPARE( tags.count(), 4 );
QVERIFY( !tags.contains( "purple" ) );

//add some symbols
QVERIFY( mStyle->saveSymbol( "symbol1", QgsMarkerSymbolV2::createSimple( QgsStringMap() ), 0, QStringList() << "red" << "starry" ) );
mStyle->addSymbol( "blue starry", QgsMarkerSymbolV2::createSimple( QgsStringMap() ), true );
mStyle->addSymbol( "red circle", QgsMarkerSymbolV2::createSimple( QgsStringMap() ), true );

//tag them
QVERIFY( mStyle->tagSymbol( QgsStyleV2::SymbolEntity, "blue starry", QStringList() << "blue" << "starry" ) );
QVERIFY( mStyle->tagSymbol( QgsStyleV2::SymbolEntity, "red circle", QStringList() << "red" << "circle" ) );
//bad symbol name
QVERIFY( !mStyle->tagSymbol( QgsStyleV2::SymbolEntity, "no symbol", QStringList() << "red" << "circle" ) );
//tag which hasn't been added yet
QVERIFY( mStyle->tagSymbol( QgsStyleV2::SymbolEntity, "red circle", QStringList() << "round" ) );
tags = mStyle->tags();
QVERIFY( tags.contains( "round" ) );

//check that tags have been applied
tags = mStyle->tagsOfSymbol( QgsStyleV2::SymbolEntity, "blue starry" );
QCOMPARE( tags.count(), 2 );
QVERIFY( tags.contains( "blue" ) );
QVERIFY( tags.contains( "starry" ) );
tags = mStyle->tagsOfSymbol( QgsStyleV2::SymbolEntity, "red circle" );
QCOMPARE( tags.count(), 3 );
QVERIFY( tags.contains( "red" ) );
QVERIFY( tags.contains( "circle" ) );
QVERIFY( tags.contains( "round" ) );
tags = mStyle->tagsOfSymbol( QgsStyleV2::SymbolEntity, "symbol1" );
QCOMPARE( tags.count(), 2 );
QVERIFY( tags.contains( "red" ) );
QVERIFY( tags.contains( "starry" ) );

//remove a tag, including a non-present tag
QVERIFY( mStyle->detagSymbol( QgsStyleV2::SymbolEntity, "blue starry", QStringList() << "bad" << "blue" ) );
tags = mStyle->tagsOfSymbol( QgsStyleV2::SymbolEntity, "blue starry" );
QCOMPARE( tags.count(), 1 );
QVERIFY( tags.contains( "starry" ) );

//try to remove tag from non-existing symbol
QVERIFY( !mStyle->detagSymbol( QgsStyleV2::SymbolEntity, "no symbol!", QStringList() << "bad" << "blue" ) );

//check symbols with tag
QStringList symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "red" ) );
QCOMPARE( symbols.count(), 2 );
QVERIFY( symbols.contains( "symbol1" ) );
QVERIFY( symbols.contains( "red circle" ) );
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "starry" ) );
QCOMPARE( symbols.count(), 2 );
QVERIFY( symbols.contains( "symbol1" ) );
QVERIFY( symbols.contains( "blue starry" ) );
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "circle" ) );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "red circle" ) );
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "round" ) );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "red circle" ) );
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "blue" ) );
QVERIFY( symbols.isEmpty() );
symbols = mStyle->symbolsWithTag( QgsStyleV2::SymbolEntity, mStyle->tagId( "no tag" ) );
QVERIFY( symbols.isEmpty() );

//searching returns symbols with matching tags
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "red" );
QCOMPARE( symbols.count(), 2 );
QVERIFY( symbols.contains( "symbol1" ) );
QVERIFY( symbols.contains( "red circle" ) );
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "symbol1" );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "symbol1" ) );
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "starry" );
QCOMPARE( symbols.count(), 2 );
QVERIFY( symbols.contains( "symbol1" ) );
QVERIFY( symbols.contains( "blue starry" ) );
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "blue" );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "blue starry" ) );
symbols = mStyle->findSymbols( QgsStyleV2::SymbolEntity, "round" );
QCOMPARE( symbols.count(), 1 );
QVERIFY( symbols.contains( "red circle" ) );
}

QTEST_MAIN( TestStyleV2 )
#include "testqgsstylev2.moc"

0 comments on commit 6be8d06

Please sign in to comment.