Skip to content

Commit

Permalink
Converted symbology style from XML to SQlite
Browse files Browse the repository at this point in the history
- Adds a python script, and the resulting default .db file
- Cannot SAVE new symbols into the DB still
Added the Grouping of symbols in Style Manager
- Can read groups from the DB and load them
- Limited to read-only operations
  • Loading branch information
Arunmozhi committed Jul 9, 2012
1 parent bb0b6e4 commit ca105e4
Show file tree
Hide file tree
Showing 10 changed files with 514 additions and 192 deletions.
2 changes: 1 addition & 1 deletion resources/CMakeLists.txt
@@ -1,4 +1,4 @@
INSTALL(FILES srs.db qgis.db qgis_help.db symbology-ng-style.xml spatialite.db customization.xml
INSTALL(FILES srs.db qgis.db qgis_help.db symbology-ng-style.db spatialite.db customization.xml
DESTINATION ${QGIS_DATA_DIR}/resources)

ADD_SUBDIRECTORY(context_help)
Expand Down
Binary file added resources/symbology-ng-style.db
Binary file not shown.
95 changes: 95 additions & 0 deletions scripts/symbol_xml2db.py
@@ -0,0 +1,95 @@
#!/usr/bin/python
"""
/***************************************************************************
symbol_xml2db.py
-------------------
begin : 26-5-2012
copyright : (C) 2012 by Arunmozhi
email : aruntheguy at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
The script creates a sqlite3 Db for storing the symbols which will be
shipped with QGIS by default. It then converts symbols and colorramps
in the symbology_ng_style.xml to the database table entries.
"""
import sqlite3

from xml.dom.minidom import parse, parseString

xmlfile = "../resources/symbology-ng-style.xml"
dbfile = "../resources/symbology-ng-style.db"

_symbol = "CREATE TABLE symbol("\
"id INTEGER PRIMARY KEY,"\
"name TEXT UNIQUE,"\
"xml TEXT,"\
"groupid INTEGER)"

_colorramp = "CREATE TABLE colorramp("\
"id INTEGER PRIMARY KEY,"\
"name TEXT,"\
"xml TEXT)"

_tag = "CREATE TABLE tag("\
"id INTEGER PRIMARY KEY,"\
"name TEXT)"

_tagmap = "CREATE TABLE tagmap("\
"tag_id INTEGER NOT NULL,"\
"symbol_id INTEGER)"

_symgroup = "CREATE TABLE symgroup("\
"id INTEGER PRIMARY KEY,"\
"name TEXT,"\
"parent INTEGER)"

create_tables = [ _symbol, _colorramp, _tag, _tagmap, _symgroup ]

# Create the DB with required Schema
conn = sqlite3.connect( dbfile )
c = conn.cursor()
print "Creating tables in the Database\n"
for table in create_tables:
try:
c.execute( table )
print table
except sqlite3.OperationalError as e:
pass
conn.commit()

# parse the XML file & write symbol into DB
dom = parse( xmlfile )
symbols = dom.getElementsByTagName( "symbol" )
for symbol in symbols:
symbol_name = symbol.getAttribute( "name" )
if '@' in symbol_name:
parts = symbol_name.split('@')
parent_name = parts[1]
layerno = int(parts[2])
c.execute( "SELECT xml FROM symbol WHERE name=(?)", (parent_name,) )
symdom = parseString( c.fetchone()[0] ).getElementsByTagName( 'symbol' )[0]
symdom.getElementsByTagName( "layer" )[ layerno ].appendChild( symbol )
c.execute( "UPDATE symbol SET xml=? WHERE name=?", ( symdom.toxml(), parent_name ))
else:
c.execute( "INSERT INTO symbol VALUES (?,?,?,?)", ( None, symbol_name, symbol.toxml(), None ) )
conn.commit()


# ColorRamps
colorramps = dom.getElementsByTagName( "colorramp" )
for ramp in colorramps:
ramp_name = ramp.getAttribute( "name" )
c.execute( "INSERT INTO colorramp VALUES (?,?,?)", ( None, ramp_name, ramp.toxml() ) )
conn.commit()

# Finally close the sqlite cursor
c.close()
4 changes: 2 additions & 2 deletions src/core/qgsapplication.cpp
Expand Up @@ -516,12 +516,12 @@ const QString QgsApplication::svgPath()

const QString QgsApplication::userStyleV2Path()
{
return qgisSettingsDirPath() + QString( "symbology-ng-style.xml" );
return qgisSettingsDirPath() + QString( "symbology-ng-style.db" );
}

const QString QgsApplication::defaultStyleV2Path()
{
return ABISYM( mPkgDataPath ) + QString( "/resources/symbology-ng-style.xml" );
return ABISYM( mPkgDataPath ) + QString( "/resources/symbology-ng-style.db" );
}

const QString QgsApplication::libraryPath()
Expand Down
168 changes: 121 additions & 47 deletions src/core/symbology-ng/qgsstylev2.cpp
Expand Up @@ -27,6 +27,9 @@
#include <QDomElement>
#include <QFile>
#include <QTextStream>
#include <QByteArray>

#include <sqlite3.h>

#define STYLE_CURRENT_VERSION "0"

Expand Down Expand Up @@ -167,69 +170,71 @@ QStringList QgsStyleV2::colorRampNames()
return mColorRamps.keys();
}


bool QgsStyleV2::load( QString filename )
sqlite3* QgsStyleV2::openDB( QString filename )
{
mErrorString = QString();
sqlite3 *db;
int rc;

// import xml file
QDomDocument doc( "style" );
QFile f( filename );
if ( !f.open( QFile::ReadOnly ) )
QByteArray namearray = filename.toUtf8();
rc = sqlite3_open( namearray.constData(), &db );
if ( rc )
{
mErrorString = "Couldn't open the style file: " + filename;
return false;
mErrorString = "Couldn't open the style DB: " + QString( sqlite3_errmsg( db ) );
sqlite3_close( db );
return NULL;
}

// parse the document
if ( !doc.setContent( &f ) )
{
mErrorString = "Couldn't parse the style file: " + filename;
f.close();
return false;
}
f.close();
return db;
}

QDomElement docElem = doc.documentElement();
if ( docElem.tagName() != "qgis_style" )
{
mErrorString = "Incorrect root tag in style: " + docElem.tagName();
return false;
}
bool QgsStyleV2::load( QString filename )
{
mErrorString = QString();

// check for style version
QString version = docElem.attribute( "version" );
if ( version != STYLE_CURRENT_VERSION )
{
mErrorString = "Unknown style file version: " + version;
// Open the sqlite DB
sqlite3* db = openDB( filename );
if ( db == NULL )
return false;
}

// load symbols
QDomElement symbolsElement = docElem.firstChildElement( "symbols" );
if ( !symbolsElement.isNull() )
// First create all the Main symbols
sqlite3_stmt *ppStmt;
const char *query = "SELECT * FROM symbol;";
int nError = sqlite3_prepare_v2( db, query, -1, &ppStmt, NULL );
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
mSymbols = QgsSymbolLayerV2Utils::loadSymbols( symbolsElement );
QDomDocument doc;
QString symbol_name = QString( reinterpret_cast<const char*>( sqlite3_column_text( ppStmt, SymbolName ) ) );
QString xmlstring = QString( reinterpret_cast<const char*>( sqlite3_column_text( ppStmt, SymbolXML ) ) );
if( !doc.setContent( xmlstring ) )
{
QgsDebugMsg( "Cannot open symbol" + symbol_name );
continue;
}
QDomElement symElement = doc.documentElement();
QgsSymbolV2 *symbol = QgsSymbolLayerV2Utils::loadSymbol( symElement );
if ( symbol != NULL )
mSymbols.insert( symbol_name, symbol );
}
sqlite3_finalize( ppStmt );

// load color ramps
QDomElement rampsElement = docElem.firstChildElement( "colorramps" );
QDomElement e = rampsElement.firstChildElement();
while ( !e.isNull() )
const char *rquery = "SELECT * FROM colorramp;";
nError = sqlite3_prepare_v2( db, rquery, -1, &ppStmt, NULL );
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
if ( e.tagName() == "colorramp" )
QDomDocument doc;
QString ramp_name = QString( reinterpret_cast<const char*>( sqlite3_column_text( ppStmt, ColorrampName ) ) );
QString xmlstring = QString( reinterpret_cast<const char*>( sqlite3_column_text( ppStmt, ColorrampXML ) ) );
if( !doc.setContent( xmlstring ) )
{
QgsVectorColorRampV2* ramp = QgsSymbolLayerV2Utils::loadColorRamp( e );
if ( ramp != NULL )
addColorRamp( e.attribute( "name" ), ramp );
QgsDebugMsg( "Cannot open symbol" + ramp_name );
continue;
}
else
{
QgsDebugMsg( "unknown tag: " + e.tagName() );
}
e = e.nextSiblingElement();
QDomElement rampElement = doc.documentElement();
QgsVectorColorRampV2* ramp = QgsSymbolLayerV2Utils::loadColorRamp( rampElement );
if ( ramp != NULL )
addColorRamp( ramp_name, ramp );
}

sqlite3_close( db );
mFileName = filename;
return true;
}
Expand All @@ -242,6 +247,9 @@ bool QgsStyleV2::save( QString filename )
if ( filename.isEmpty() )
filename = mFileName;

// TODO evaluate the requirement of this function and change implementation accordingly

/*
QDomDocument doc( "qgis_style" );
QDomElement root = doc.createElement( "qgis_style" );
root.setAttribute( "version", STYLE_CURRENT_VERSION );
Expand Down Expand Up @@ -271,6 +279,7 @@ bool QgsStyleV2::save( QString filename )
QTextStream ts( &f );
doc.save( ts, 2 );
f.close();
*/

mFileName = filename;
return true;
Expand All @@ -293,3 +302,68 @@ bool QgsStyleV2::renameColorRamp( QString oldName, QString newName )
mColorRamps.insert( newName, mColorRamps.take( oldName ) );
return true;
}

QgsSymbolGroupMap QgsStyleV2::groupNames( QString parent )
{
QgsDebugMsg( "Request for groupNames for parent " + parent );
// get the name list from the sqlite db and return as a QStringList
sqlite3* db = openDB( mFileName );
if( db == NULL )
{
QgsDebugMsg( "Cannot open database for listing groups" );
return QgsSymbolGroupMap();
}
sqlite3_stmt *ppStmt;
int nError;
char *query;

if ( parent == "" || parent == QString() )
{
query = sqlite3_mprintf( "SELECT * FROM symgroup WHERE parent IS NULL;" );
}
else
{
QByteArray parentArray = parent.toUtf8();
char *subquery = sqlite3_mprintf( "SELECT * FROM symgroup WHERE name='%q';", parentArray.constData() );
nError = sqlite3_prepare_v2( db, subquery, -1, &ppStmt, NULL );
if ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
query = sqlite3_mprintf( "SELECT * FROM symgroup WHERE parent=%d;", sqlite3_column_int( ppStmt, SymgroupId ) );
}
sqlite3_finalize( ppStmt );
}

QgsSymbolGroupMap groupNames;

nError = sqlite3_prepare_v2( db, query, -1, &ppStmt, NULL );
while ( nError == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
QString group = QString( reinterpret_cast<const char*>( sqlite3_column_text( ppStmt, SymgroupName ) ) );
groupNames.insert( sqlite3_column_int( ppStmt, SymgroupId ), group );
}
sqlite3_finalize( ppStmt );
sqlite3_close( db );
return groupNames;
}

QStringList QgsStyleV2::symbolsOfGroup( int groupid )
{
sqlite3 *db = openDB( mFileName );
if( db == NULL )
{
QgsDebugMsg( "Cannot Open Db for getting group symbols of groupid: " + groupid );
return QStringList();
}

QStringList symbols;
sqlite3_stmt *ppStmt;
char *query = sqlite3_mprintf( "SELECT name FROM symbol WHERE groupid=%d;", groupid );
int nErr = sqlite3_prepare_v2( db, query, -1, &ppStmt, NULL );
while ( nErr == SQLITE_OK && sqlite3_step( ppStmt ) == SQLITE_ROW )
{
QString symbol = QString( reinterpret_cast<const char*>( sqlite3_column_text( ppStmt, 0 ) ) );
symbols.append( symbol );
}

return symbols;
}
21 changes: 21 additions & 0 deletions src/core/symbology-ng/qgsstylev2.h
Expand Up @@ -19,6 +19,8 @@
#include <QMap>
#include <QString>

#include <sqlite3.h>

#include "qgssymbollayerv2utils.h" // QgsStringMap

class QgsSymbolV2;
Expand All @@ -29,6 +31,15 @@ class QDomDocument;
class QDomElement;

typedef QMap<QString, QgsVectorColorRampV2* > QgsVectorColorRampV2Map;
typedef QMap<int, QString> QgsSymbolGroupMap;

// Enumeraters representing sqlite DB columns
enum SymbolTable { SymbolId, SymbolName, SymbolXML, SymbolGroupId };
enum SymgroupTable { SymgroupId, SymgroupName, SymgroupParent };
enum TagTable { TagId, TagName };
enum TagmapTable { TagmapTagId, TagmapSymbolId };
enum ColorrampTable { ColorrampId, ColorrampName, ColorrampXML };


class CORE_EXPORT QgsStyleV2
{
Expand Down Expand Up @@ -65,6 +76,13 @@ class CORE_EXPORT QgsStyleV2
//! return a list of names of symbols
QStringList symbolNames();

//! return a map of all groupid and names for the given parent
//! Returns the First order groups when empty QString is passed
QgsSymbolGroupMap groupNames( QString parent = "" );

//! returns the symbolnames of a given groupid
QStringList symbolsOfGroup( int groupid );


//! add color ramp to style. takes ramp's ownership
bool addColorRamp( QString name, QgsVectorColorRampV2* colorRamp );
Expand Down Expand Up @@ -110,6 +128,9 @@ class CORE_EXPORT QgsStyleV2
QString mFileName;

static QgsStyleV2* mDefaultStyle;

//! Convinence function to open the DB and return a sqlite3 object
sqlite3* openDB( QString filename );
};


Expand Down

0 comments on commit ca105e4

Please sign in to comment.