QGIS/src/core/qgsmaplayer.cpp

917 lines
26 KiB
C++

/***************************************************************************
qgsmaplayer.cpp - description
-------------------
begin : Fri Jun 28 2002
copyright : (C) 2002 by Gary E.Sherman
email : sherman at mrcc.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. *
* *
***************************************************************************/
/* $Id$ */
#include <QDateTime>
#include <QDomNode>
#include <QFileInfo>
#include <QSettings> // TODO: get rid of it [MD]
#include <QDir>
#include <QFile>
#include <QDomDocument>
#include <QDomElement>
#include <QDomImplementation>
#include <QTextStream>
#include <QUrl>
#include <sqlite3.h>
#include "qgslogger.h"
#include "qgsrectangle.h"
#include "qgssymbol.h"
#include "qgsmaplayer.h"
#include "qgscoordinatereferencesystem.h"
#include "qgsapplication.h"
#include "qgsproject.h"
#include "qgsdatasourceuri.h"
#include "qgsvectorlayer.h"
QgsMapLayer::QgsMapLayer( QgsMapLayer::LayerType type,
QString lyrname,
QString source ) :
mTransparencyLevel( 255 ), // 0 is completely transparent
mValid( false ), // assume the layer is invalid
mDataSource( source ),
mID( "" ),
mLayerType( type )
{
QgsDebugMsg( "lyrname is '" + lyrname + "'" );
mCRS = new QgsCoordinateReferenceSystem();
// Set the display name = internal name
mLayerName = capitaliseLayerName( lyrname );
QgsDebugMsg( "layerName is '" + mLayerName + "'" );
// Generate the unique ID of this layer
QDateTime dt = QDateTime::currentDateTime();
mID = lyrname + dt.toString( "yyyyMMddhhmmsszzz" );
// Tidy the ID up to avoid characters that may cause problems
// elsewhere (e.g in some parts of XML). Replaces every non-word
// character (word characters are the alphabet, numbers and
// underscore) with an underscore.
// Note that the first backslashe in the regular expression is
// there for the compiler, so the pattern is actually \W
mID.replace( QRegExp( "[\\W]" ), "_" );
//set some generous defaults for scale based visibility
mMinScale = 0;
mMaxScale = 100000000;
mScaleBasedVisibility = false;
mpCacheImage = 0;
}
QgsMapLayer::~QgsMapLayer()
{
delete mCRS;
if ( mpCacheImage )
{
delete mpCacheImage;
}
}
QgsMapLayer::LayerType QgsMapLayer::type() const
{
return mLayerType;
}
/** Get this layer's unique ID */
QString QgsMapLayer::id() const
{
return mID;
}
/** Write property of QString layerName. */
void QgsMapLayer::setLayerName( const QString & name )
{
QgsDebugMsg( "new name is '" + name + "'" );
mLayerName = capitaliseLayerName( name );
emit layerNameChanged();
}
/** Read property of QString layerName. */
QString const & QgsMapLayer::name() const
{
QgsDebugMsgLevel( "returning name '" + mLayerName + "'", 3 );
return mLayerName;
}
QString QgsMapLayer::publicSource() const
{
// Redo this every time we're asked for it, as we don't know if
// dataSource has changed.
QString safeName = QgsDataSourceURI::removePassword( mDataSource );
return safeName;
}
QString const & QgsMapLayer::source() const
{
return mDataSource;
}
QgsRectangle QgsMapLayer::extent() const
{
return mLayerExtent;
}
bool QgsMapLayer::draw( QgsRenderContext& rendererContext )
{
return false;
}
void QgsMapLayer::drawLabels( QgsRenderContext& rendererContext )
{
// QgsDebugMsg("entered.");
}
bool QgsMapLayer::readXML( QDomNode & layer_node )
{
QgsCoordinateReferenceSystem savedCRS;
CUSTOM_CRS_VALIDATION savedValidation;
bool layerError;
QDomElement element = layer_node.toElement();
QDomNode mnl;
QDomElement mne;
// read provider
QString provider;
mnl = layer_node.namedItem( "provider" );
mne = mnl.toElement();
provider = mne.text();
// set data source
mnl = layer_node.namedItem( "datasource" );
mne = mnl.toElement();
mDataSource = mne.text();
if ( provider == "spatialite" )
{
QgsDataSourceURI uri( mDataSource );
uri.setDatabase( QgsProject::instance()->readPath( uri.database() ) );
mDataSource = uri.uri();
}
else if ( provider == "ogr" )
{
QStringList theURIParts = mDataSource.split( "|" );
theURIParts[0] = QgsProject::instance()->readPath( theURIParts[0] );
mDataSource = theURIParts.join( "|" );
}
else if ( provider == "delimitedtext" )
{
QUrl urlSource = QUrl::fromEncoded( mDataSource.toAscii() );
if ( !mDataSource.startsWith( "file:" ) )
{
QUrl file = QUrl::fromLocalFile( mDataSource.left( mDataSource.indexOf( "?" ) ) );
urlSource.setScheme( "file" );
urlSource.setPath( file.path() );
}
QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->readPath( urlSource.toLocalFile() ) );
urlDest.setQueryItems( urlSource.queryItems() );
mDataSource = QString::fromAscii( urlDest.toEncoded() );
}
else
{
mDataSource = QgsProject::instance()->readPath( mDataSource );
}
// Set the CRS from project file, asking the user if necessary.
// Make it the saved CRS to have WMS layer projected correctly.
// We will still overwrite whatever GDAL etc picks up anyway
// further down this function.
mnl = layer_node.namedItem( "layername" );
mne = mnl.toElement();
QDomNode srsNode = layer_node.namedItem( "srs" );
mCRS->readXML( srsNode );
mCRS->setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
mCRS->validate();
savedCRS = *mCRS;
// Do not validate any projections in children, they will be overwritten anyway.
// No need to ask the user for a projections when it is overwritten, is there?
savedValidation = QgsCoordinateReferenceSystem::customSrsValidation();
QgsCoordinateReferenceSystem::setCustomSrsValidation( NULL );
// now let the children grab what they need from the Dom node.
layerError = !readXml( layer_node );
// overwrite CRS with what we read from project file before the raster/vector
// file readnig functions changed it. They will if projections is specfied in the file.
// FIXME: is this necessary?
QgsCoordinateReferenceSystem::setCustomSrsValidation( savedValidation );
*mCRS = savedCRS;
// Abort if any error in layer, such as not found.
if ( layerError )
{
return false;
}
// the internal name is just the data source basename
//QFileInfo dataSourceFileInfo( mDataSource );
//internalName = dataSourceFileInfo.baseName();
// set ID
mnl = layer_node.namedItem( "id" );
if ( ! mnl.isNull() )
{
mne = mnl.toElement();
if ( ! mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
{
mID = mne.text();
}
}
// use scale dependent visibility flag
toggleScaleBasedVisibility( element.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
setMinimumScale( element.attribute( "minimumScale" ).toFloat() );
setMaximumScale( element.attribute( "maximumScale" ).toFloat() );
// set name
mnl = layer_node.namedItem( "layername" );
mne = mnl.toElement();
setLayerName( mne.text() );
//read transparency level
QDomNode transparencyNode = layer_node.namedItem( "transparencyLevelInt" );
if ( ! transparencyNode.isNull() )
{
// set transparency level only if it's in project
// (otherwise it sets the layer transparent)
QDomElement myElement = transparencyNode.toElement();
setTransparency( myElement.text().toInt() );
}
readCustomProperties( layer_node );
return true;
} // void QgsMapLayer::readXML
bool QgsMapLayer::readXml( QDomNode & layer_node )
{
// NOP by default; children will over-ride with behavior specific to them
return true;
} // void QgsMapLayer::readXml
bool QgsMapLayer::writeXML( QDomNode & layer_node, QDomDocument & document )
{
// general layer metadata
QDomElement maplayer = document.createElement( "maplayer" );
// use scale dependent visibility flag
maplayer.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
maplayer.setAttribute( "minimumScale", minimumScale() );
maplayer.setAttribute( "maximumScale", maximumScale() );
// ID
QDomElement layerId = document.createElement( "id" );
QDomText layerIdText = document.createTextNode( id() );
layerId.appendChild( layerIdText );
maplayer.appendChild( layerId );
// data source
QDomElement dataSource = document.createElement( "datasource" );
QString src = source();
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
if ( vlayer && vlayer->providerType() == "spatialite" )
{
QgsDataSourceURI uri( src );
QString database = QgsProject::instance()->writePath( uri.database() );
uri.setConnection( uri.host(), uri.port(), database, uri.username(), uri.password() );
src = uri.uri();
}
else if ( vlayer && vlayer->providerType() == "ogr" )
{
QStringList theURIParts = src.split( "|" );
theURIParts[0] = QgsProject::instance()->writePath( theURIParts[0] );
src = theURIParts.join( "|" );
}
else if ( vlayer && vlayer->providerType() == "delimitedtext" )
{
QUrl urlSource = QUrl::fromEncoded( src.toAscii() );
QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->writePath( urlSource.toLocalFile() ) );
urlDest.setQueryItems( urlSource.queryItems() );
src = QString::fromAscii( urlDest.toEncoded() );
}
else
{
src = QgsProject::instance()->writePath( src );
}
QDomText dataSourceText = document.createTextNode( src );
dataSource.appendChild( dataSourceText );
maplayer.appendChild( dataSource );
// layer name
QDomElement layerName = document.createElement( "layername" );
QDomText layerNameText = document.createTextNode( name() );
layerName.appendChild( layerNameText );
maplayer.appendChild( layerName );
// timestamp if supported
if ( timestamp() > QDateTime() )
{
QDomElement stamp = document.createElement( "timestamp" );
QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
stamp.appendChild( stampText );
maplayer.appendChild( stamp );
}
maplayer.appendChild( layerName );
// zorder
// This is no longer stored in the project file. It is superfluous since the layers
// are written and read in the proper order.
// spatial reference system id
QDomElement mySrsElement = document.createElement( "srs" );
mCRS->writeXML( mySrsElement, document );
maplayer.appendChild( mySrsElement );
// <transparencyLevelInt>
QDomElement transparencyLevelIntElement = document.createElement( "transparencyLevelInt" );
QDomText transparencyLevelIntText = document.createTextNode( QString::number( getTransparency() ) );
transparencyLevelIntElement.appendChild( transparencyLevelIntText );
maplayer.appendChild( transparencyLevelIntElement );
// now append layer node to map layer node
layer_node.appendChild( maplayer );
writeCustomProperties( maplayer, document );
return writeXml( maplayer, document );
} // bool QgsMapLayer::writeXML
bool QgsMapLayer::writeXml( QDomNode & layer_node, QDomDocument & document )
{
// NOP by default; children will over-ride with behavior specific to them
return true;
} // void QgsMapLayer::writeXml
bool QgsMapLayer::isValid()
{
return mValid;
}
void QgsMapLayer::invalidTransformInput()
{
QgsDebugMsg( "called" );
// TODO: emit a signal - it will be used to update legend
}
QString QgsMapLayer::lastErrorTitle()
{
return QString();
}
QString QgsMapLayer::lastError()
{
return QString();
}
void QgsMapLayer::connectNotify( const char * signal )
{
QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
} // QgsMapLayer::connectNotify
void QgsMapLayer::toggleScaleBasedVisibility( bool theVisibilityFlag )
{
mScaleBasedVisibility = theVisibilityFlag;
}
bool QgsMapLayer::hasScaleBasedVisibility()
{
return mScaleBasedVisibility;
}
void QgsMapLayer::setMinimumScale( float theMinScale )
{
mMinScale = theMinScale;
}
float QgsMapLayer::minimumScale()
{
return mMinScale;
}
void QgsMapLayer::setMaximumScale( float theMaxScale )
{
mMaxScale = theMaxScale;
}
float QgsMapLayer::maximumScale()
{
return mMaxScale;
}
QStringList QgsMapLayer::subLayers()
{
return QStringList(); // Empty
}
void QgsMapLayer::setLayerOrder( QStringList layers )
{
// NOOP
}
void QgsMapLayer::setSubLayerVisibility( QString name, bool vis )
{
// NOOP
}
const QgsCoordinateReferenceSystem& QgsMapLayer::crs()
{
return *mCRS;
}
const QgsCoordinateReferenceSystem& QgsMapLayer::srs()
{
// This will be dropped in QGIS 2.0 due to conflicting name
// Please use crs() in the future
return *mCRS;
}
void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem& srs, bool emitSignal )
{
*mCRS = srs;
if ( emitSignal )
emit layerCrsChanged();
}
unsigned int QgsMapLayer::getTransparency()
{
return mTransparencyLevel;
}
void QgsMapLayer::setTransparency( unsigned int theInt )
{
mTransparencyLevel = theInt;
}
QString QgsMapLayer::capitaliseLayerName( const QString name )
{
// Capitalise the first letter of the layer name if requested
QSettings settings;
bool capitaliseLayerName =
settings.value( "qgis/capitaliseLayerName", QVariant( false ) ).toBool();
QString layerName( name );
if ( capitaliseLayerName )
layerName = layerName.left( 1 ).toUpper() + layerName.mid( 1 );
return layerName;
}
QString QgsMapLayer::loadDefaultStyle( bool & theResultFlag )
{
QString myURI = publicSource();
QFileInfo myFileInfo( myURI );
QString key;
if ( myFileInfo.exists() )
{
// get the file name for our .qml style file
key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
}
else
{
key = myURI;
}
return loadNamedStyle( key, theResultFlag );
}
bool QgsMapLayer::loadNamedStyleFromDb( const QString db, const QString theURI, QString &qml )
{
bool theResultFlag = false;
// read from database
sqlite3 *myDatabase;
sqlite3_stmt *myPreparedStatement;
const char *myTail;
int myResult;
QgsDebugMsg( QString( "Trying to load style for \"%1\" from \"%2\"" ).arg( theURI ).arg( db ) );
if ( !QFile( db ).exists() )
return false;
myResult = sqlite3_open( db.toUtf8().data(), &myDatabase );
if ( myResult != SQLITE_OK )
{
return false;
}
QString mySql = "select qml from tbl_styles where style=?";
myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
if ( myResult == SQLITE_OK )
{
QByteArray param = theURI.toUtf8();
if ( sqlite3_bind_text( myPreparedStatement, 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
{
qml = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
theResultFlag = true;
}
sqlite3_finalize( myPreparedStatement );
}
sqlite3_close( myDatabase );
return theResultFlag;
}
QString QgsMapLayer::loadNamedStyle( const QString theURI, bool &theResultFlag )
{
theResultFlag = false;
QDomDocument myDocument( "qgis" );
// location of problem associated with errorMsg
int line, column;
QString myErrorMessage;
QFile myFile( theURI );
if ( myFile.open( QFile::ReadOnly ) )
{
// read file
theResultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
if ( !theResultFlag )
myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
myFile.close();
}
else
{
QFileInfo project( QgsProject::instance()->fileName() );
QgsDebugMsg( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ) );
QString qml;
if ( loadNamedStyleFromDb( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ), theURI, qml ) ||
( project.exists() && loadNamedStyleFromDb( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), theURI, qml ) ) ||
loadNamedStyleFromDb( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( "resources/qgis.qmldb" ), theURI, qml ) )
{
theResultFlag = myDocument.setContent( qml, &myErrorMessage, &line, &column );
if ( !theResultFlag )
{
myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
}
}
else
{
myErrorMessage = tr( "style not found in database" );
}
}
if ( !theResultFlag )
{
return myErrorMessage;
}
// now get the layer node out and pass it over to the layer
// to deserialise...
QDomElement myRoot = myDocument.firstChildElement( "qgis" );
if ( myRoot.isNull() )
{
myErrorMessage = tr( "Error: qgis element could not be found in %1" ).arg( theURI );
theResultFlag = false;
return myErrorMessage;
}
// use scale dependent visibility flag
toggleScaleBasedVisibility( myRoot.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
setMinimumScale( myRoot.attribute( "minimumScale" ).toFloat() );
setMaximumScale( myRoot.attribute( "maximumScale" ).toFloat() );
//read transparency level
QDomNode transparencyNode = myRoot.namedItem( "transparencyLevelInt" );
if ( ! transparencyNode.isNull() )
{
// set transparency level only if it's in project
// (otherwise it sets the layer transparent)
QDomElement myElement = transparencyNode.toElement();
setTransparency( myElement.text().toInt() );
}
QString errorMsg;
theResultFlag = readSymbology( myRoot, errorMsg );
if ( !theResultFlag )
{
myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( theURI ).arg( errorMsg );
return myErrorMessage;
}
return "";
}
QString QgsMapLayer::saveDefaultStyle( bool & theResultFlag )
{
return saveNamedStyle( publicSource(), theResultFlag );
}
QString QgsMapLayer::saveNamedStyle( const QString theURI, bool & theResultFlag )
{
QString myErrorMessage;
QDomImplementation DomImplementation;
QDomDocumentType documentType =
DomImplementation.createDocumentType(
"qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" );
QDomDocument myDocument( documentType );
QDomElement myRootNode = myDocument.createElement( "qgis" );
myRootNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
myDocument.appendChild( myRootNode );
// use scale dependent visibility flag
myRootNode.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
myRootNode.setAttribute( "minimumScale", minimumScale() );
myRootNode.setAttribute( "maximumScale", maximumScale() );
// <transparencyLevelInt>
QDomElement transparencyLevelIntElement = myDocument.createElement( "transparencyLevelInt" );
QDomText transparencyLevelIntText = myDocument.createTextNode( QString::number( getTransparency() ) );
transparencyLevelIntElement.appendChild( transparencyLevelIntText );
myRootNode.appendChild( transparencyLevelIntElement );
// now append layer node to map layer node
QString errorMsg;
if ( !writeSymbology( myRootNode, myDocument, errorMsg ) )
{
return tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
}
// check if the uri is a file or ends with .qml,
// which indicates that it should become one
// everything else goes to the database
QString filename;
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
if ( vlayer && vlayer->providerType() == "ogr" )
{
QStringList theURIParts = theURI.split( "|" );
filename = theURIParts[0];
}
else if ( vlayer && vlayer->providerType() == "delimitedtext" )
{
filename = QUrl::fromEncoded( theURI.toAscii() ).toLocalFile();
}
else
{
filename = theURI;
}
QFileInfo myFileInfo( filename );
if ( myFileInfo.exists() || filename.endsWith( ".qml", Qt::CaseInsensitive ) )
{
QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
if ( !myDirInfo.isWritable() )
{
return tr( "The directory containing your dataset needs to be writeable!" );
}
// now construct the file name for our .qml style file
QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
QFile myFile( myFileName );
if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
{
QTextStream myFileStream( &myFile );
// save as utf-8 with 2 spaces for indents
myDocument.save( myFileStream, 2 );
myFile.close();
theResultFlag = true;
return tr( "Created default style file as %1" ).arg( myFileName );
}
else
{
theResultFlag = false;
return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
}
}
else
{
QString qml = myDocument.toString();
// read from database
sqlite3 *myDatabase;
sqlite3_stmt *myPreparedStatement;
const char *myTail;
int myResult;
myResult = sqlite3_open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ).toUtf8().data(), &myDatabase );
if ( myResult != SQLITE_OK )
{
return tr( "User database could not be opened." );
}
QByteArray param0 = theURI.toUtf8();
QByteArray param1 = qml.toUtf8();
QString mySql = "create table if not exists tbl_styles(style varchar primary key,qml varchar)";
myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
if ( myResult == SQLITE_OK )
{
if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
{
sqlite3_finalize( myPreparedStatement );
sqlite3_close( myDatabase );
theResultFlag = false;
return tr( "The style table could not be created." );
}
}
sqlite3_finalize( myPreparedStatement );
mySql = "insert into tbl_styles(style,qml) values (?,?)";
myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
if ( myResult == SQLITE_OK )
{
if ( sqlite3_bind_text( myPreparedStatement, 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
sqlite3_bind_text( myPreparedStatement, 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
{
theResultFlag = true;
myErrorMessage = tr( "The style %1 was saved to database" ).arg( theURI );
}
}
sqlite3_finalize( myPreparedStatement );
if ( !theResultFlag )
{
QString mySql = "update tbl_styles set qml=? where style=?";
myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
if ( myResult == SQLITE_OK )
{
if ( sqlite3_bind_text( myPreparedStatement, 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
sqlite3_bind_text( myPreparedStatement, 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
{
theResultFlag = true;
myErrorMessage = tr( "The style %1 was updated in the database." ).arg( theURI );
}
else
{
theResultFlag = false;
myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( theURI );
}
}
else
{
theResultFlag = false;
myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( theURI );
}
sqlite3_finalize( myPreparedStatement );
}
sqlite3_close( myDatabase );
}
return myErrorMessage;
}
QUndoStack* QgsMapLayer::undoStack()
{
return &mUndoStack;
}
void QgsMapLayer::setCustomProperty( const QString& key, const QVariant& value )
{
mCustomProperties[key] = value;
}
QVariant QgsMapLayer::customProperty( const QString& value, const QVariant& defaultValue ) const
{
return mCustomProperties.value( value, defaultValue );
}
void QgsMapLayer::removeCustomProperty( const QString& key )
{
mCustomProperties.remove( key );
}
void QgsMapLayer::readCustomProperties( const QDomNode& layerNode, const QString& keyStartsWith )
{
QDomNode propsNode = layerNode.namedItem( "customproperties" );
if ( propsNode.isNull() ) // no properties stored...
return;
mCustomProperties.clear();
QDomNodeList nodes = propsNode.childNodes();
for ( int i = 0; i < nodes.size(); i++ )
{
QDomNode propNode = nodes.at( i );
if ( propNode.isNull() || propNode.nodeName() != "property" )
continue;
QDomElement propElement = propNode.toElement();
QString key = propElement.attribute( "key" );
if ( key.isEmpty() || key.startsWith( keyStartsWith ) )
{
QString value = propElement.attribute( "value" );
mCustomProperties[key] = QVariant( value );
}
}
}
void QgsMapLayer::writeCustomProperties( QDomNode & layerNode, QDomDocument & doc ) const
{
QDomElement propsElement = doc.createElement( "customproperties" );
for ( QMap<QString, QVariant>::const_iterator it = mCustomProperties.constBegin(); it != mCustomProperties.constEnd(); ++it )
{
QDomElement propElement = doc.createElement( "property" );
propElement.setAttribute( "key", it.key() );
propElement.setAttribute( "value", it.value().toString() );
propsElement.appendChild( propElement );
}
layerNode.appendChild( propsElement );
}
void QgsMapLayer::setCacheImage( QImage * thepImage )
{
QgsDebugMsg( "cache Image set!" );
if ( mpCacheImage == thepImage )
return;
if ( mpCacheImage )
{
delete mpCacheImage;
}
mpCacheImage = thepImage;
}
bool QgsMapLayer::isEditable() const
{
return false;
}
void QgsMapLayer::setValid( bool valid )
{
mValid = valid;
}
void QgsMapLayer::clearCacheImage()
{
setCacheImage( 0 );
}
QString QgsMapLayer::metadata()
{
return QString();
}