Quantum GIS API Documentation  1.7.4
src/core/qgsmaplayer.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002                           qgsmaplayer.cpp  -  description
00003                              -------------------
00004     begin                : Fri Jun 28 2002
00005     copyright            : (C) 2002 by Gary E.Sherman
00006     email                : sherman at mrcc.com
00007 ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 /* $Id$ */
00018 
00019 
00020 #include <QDateTime>
00021 #include <QDomNode>
00022 #include <QFileInfo>
00023 #include <QSettings> // TODO: get rid of it [MD]
00024 #include <QDir>
00025 #include <QFile>
00026 #include <QDomDocument>
00027 #include <QDomElement>
00028 #include <QDomImplementation>
00029 #include <QTextStream>
00030 #include <QUrl>
00031 
00032 #include <sqlite3.h>
00033 
00034 #include "qgslogger.h"
00035 #include "qgsrectangle.h"
00036 #include "qgssymbol.h"
00037 #include "qgsmaplayer.h"
00038 #include "qgscoordinatereferencesystem.h"
00039 #include "qgsapplication.h"
00040 #include "qgsproject.h"
00041 #include "qgsdatasourceuri.h"
00042 #include "qgsvectorlayer.h"
00043 
00044 QgsMapLayer::QgsMapLayer( QgsMapLayer::LayerType type,
00045                           QString lyrname,
00046                           QString source ) :
00047     mTransparencyLevel( 255 ), // 0 is completely transparent
00048     mValid( false ), // assume the layer is invalid
00049     mDataSource( source ),
00050     mID( "" ),
00051     mLayerType( type )
00052 
00053 {
00054   QgsDebugMsg( "lyrname is '" + lyrname + "'" );
00055 
00056   mCRS = new QgsCoordinateReferenceSystem();
00057 
00058   // Set the display name = internal name
00059   mLayerName = capitaliseLayerName( lyrname );
00060   QgsDebugMsg( "layerName is '" + mLayerName + "'" );
00061 
00062   // Generate the unique ID of this layer
00063   QDateTime dt = QDateTime::currentDateTime();
00064   mID = lyrname + dt.toString( "yyyyMMddhhmmsszzz" );
00065   // Tidy the ID up to avoid characters that may cause problems
00066   // elsewhere (e.g in some parts of XML). Replaces every non-word
00067   // character (word characters are the alphabet, numbers and
00068   // underscore) with an underscore.
00069   // Note that the first backslashe in the regular expression is
00070   // there for the compiler, so the pattern is actually \W
00071   mID.replace( QRegExp( "[\\W]" ), "_" );
00072 
00073   //set some generous  defaults for scale based visibility
00074   mMinScale = 0;
00075   mMaxScale = 100000000;
00076   mScaleBasedVisibility = false;
00077   mpCacheImage = 0;
00078 }
00079 
00080 
00081 
00082 QgsMapLayer::~QgsMapLayer()
00083 {
00084   delete mCRS;
00085   if ( mpCacheImage )
00086   {
00087     delete mpCacheImage;
00088   }
00089 }
00090 
00091 QgsMapLayer::LayerType QgsMapLayer::type() const
00092 {
00093   return mLayerType;
00094 }
00095 
00097 QString QgsMapLayer::id() const
00098 {
00099   return mID;
00100 }
00101 
00103 void QgsMapLayer::setLayerName( const QString & name )
00104 {
00105   QgsDebugMsg( "new name is '" + name + "'" );
00106   mLayerName = capitaliseLayerName( name );
00107   emit layerNameChanged();
00108 }
00109 
00111 QString const & QgsMapLayer::name() const
00112 {
00113   QgsDebugMsgLevel( "returning name '" + mLayerName + "'", 3 );
00114   return mLayerName;
00115 }
00116 
00117 QString QgsMapLayer::publicSource() const
00118 {
00119   // Redo this every time we're asked for it, as we don't know if
00120   // dataSource has changed.
00121   QString safeName = QgsDataSourceURI::removePassword( mDataSource );
00122   return safeName;
00123 }
00124 
00125 QString const & QgsMapLayer::source() const
00126 {
00127   return mDataSource;
00128 }
00129 
00130 QgsRectangle QgsMapLayer::extent() const
00131 {
00132   return mLayerExtent;
00133 }
00134 
00135 bool QgsMapLayer::draw( QgsRenderContext& rendererContext )
00136 {
00137   return false;
00138 }
00139 
00140 void QgsMapLayer::drawLabels( QgsRenderContext& rendererContext )
00141 {
00142   // QgsDebugMsg("entered.");
00143 }
00144 
00145 bool QgsMapLayer::readXML( QDomNode & layer_node )
00146 {
00147   QgsCoordinateReferenceSystem savedCRS;
00148   CUSTOM_CRS_VALIDATION savedValidation;
00149   bool layerError;
00150 
00151   QDomElement element = layer_node.toElement();
00152 
00153   QDomNode mnl;
00154   QDomElement mne;
00155 
00156   // read provider
00157   QString provider;
00158   mnl = layer_node.namedItem( "provider" );
00159   mne = mnl.toElement();
00160   provider = mne.text();
00161 
00162   // set data source
00163   mnl = layer_node.namedItem( "datasource" );
00164   mne = mnl.toElement();
00165   mDataSource = mne.text();
00166 
00167   if ( provider == "spatialite" )
00168   {
00169     QgsDataSourceURI uri( mDataSource );
00170     uri.setDatabase( QgsProject::instance()->readPath( uri.database() ) );
00171     mDataSource = uri.uri();
00172   }
00173   else if ( provider == "ogr" )
00174   {
00175     QStringList theURIParts = mDataSource.split( "|" );
00176     theURIParts[0] = QgsProject::instance()->readPath( theURIParts[0] );
00177     mDataSource = theURIParts.join( "|" );
00178   }
00179   else if ( provider == "delimitedtext" )
00180   {
00181     QUrl urlSource = QUrl::fromEncoded( mDataSource.toAscii() );
00182 
00183     if ( !mDataSource.startsWith( "file:" ) )
00184     {
00185       QUrl file = QUrl::fromLocalFile( mDataSource.left( mDataSource.indexOf( "?" ) ) );
00186       urlSource.setScheme( "file" );
00187       urlSource.setPath( file.path() );
00188     }
00189 
00190     QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->readPath( urlSource.toLocalFile() ) );
00191     urlDest.setQueryItems( urlSource.queryItems() );
00192     mDataSource = QString::fromAscii( urlDest.toEncoded() );
00193   }
00194   else
00195   {
00196     mDataSource = QgsProject::instance()->readPath( mDataSource );
00197   }
00198 
00199   // Set the CRS from project file, asking the user if necessary.
00200   // Make it the saved CRS to have WMS layer projected correctly.
00201   // We will still overwrite whatever GDAL etc picks up anyway
00202   // further down this function.
00203   mnl = layer_node.namedItem( "layername" );
00204   mne = mnl.toElement();
00205 
00206   QDomNode srsNode = layer_node.namedItem( "srs" );
00207   mCRS->readXML( srsNode );
00208   mCRS->setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
00209   mCRS->validate();
00210   savedCRS = *mCRS;
00211 
00212   // Do not validate any projections in children, they will be overwritten anyway.
00213   // No need to ask the user for a projections when it is overwritten, is there?
00214   savedValidation = QgsCoordinateReferenceSystem::customSrsValidation();
00215   QgsCoordinateReferenceSystem::setCustomSrsValidation( NULL );
00216 
00217   // now let the children grab what they need from the Dom node.
00218   layerError = !readXml( layer_node );
00219 
00220   // overwrite CRS with what we read from project file before the raster/vector
00221   // file readnig functions changed it. They will if projections is specfied in the file.
00222   // FIXME: is this necessary?
00223   QgsCoordinateReferenceSystem::setCustomSrsValidation( savedValidation );
00224   *mCRS = savedCRS;
00225 
00226   // Abort if any error in layer, such as not found.
00227   if ( layerError )
00228   {
00229     return false;
00230   }
00231 
00232   // the internal name is just the data source basename
00233   //QFileInfo dataSourceFileInfo( mDataSource );
00234   //internalName = dataSourceFileInfo.baseName();
00235 
00236   // set ID
00237   mnl = layer_node.namedItem( "id" );
00238   if ( ! mnl.isNull() )
00239   {
00240     mne = mnl.toElement();
00241     if ( ! mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
00242     {
00243       mID = mne.text();
00244     }
00245   }
00246 
00247   // use scale dependent visibility flag
00248   toggleScaleBasedVisibility( element.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
00249   setMinimumScale( element.attribute( "minimumScale" ).toFloat() );
00250   setMaximumScale( element.attribute( "maximumScale" ).toFloat() );
00251 
00252   // set name
00253   mnl = layer_node.namedItem( "layername" );
00254   mne = mnl.toElement();
00255   setLayerName( mne.text() );
00256 
00257   //read transparency level
00258   QDomNode transparencyNode = layer_node.namedItem( "transparencyLevelInt" );
00259   if ( ! transparencyNode.isNull() )
00260   {
00261     // set transparency level only if it's in project
00262     // (otherwise it sets the layer transparent)
00263     QDomElement myElement = transparencyNode.toElement();
00264     setTransparency( myElement.text().toInt() );
00265   }
00266 
00267   readCustomProperties( layer_node );
00268 
00269   return true;
00270 } // void QgsMapLayer::readXML
00271 
00272 
00273 bool QgsMapLayer::readXml( QDomNode & layer_node )
00274 {
00275   // NOP by default; children will over-ride with behavior specific to them
00276 
00277   return true;
00278 } // void QgsMapLayer::readXml
00279 
00280 
00281 
00282 bool QgsMapLayer::writeXML( QDomNode & layer_node, QDomDocument & document )
00283 {
00284   // general layer metadata
00285   QDomElement maplayer = document.createElement( "maplayer" );
00286 
00287   // use scale dependent visibility flag
00288   maplayer.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
00289   maplayer.setAttribute( "minimumScale", minimumScale() );
00290   maplayer.setAttribute( "maximumScale", maximumScale() );
00291 
00292   // ID
00293   QDomElement layerId = document.createElement( "id" );
00294   QDomText layerIdText = document.createTextNode( id() );
00295   layerId.appendChild( layerIdText );
00296 
00297   maplayer.appendChild( layerId );
00298 
00299   // data source
00300   QDomElement dataSource = document.createElement( "datasource" );
00301 
00302   QString src = source();
00303 
00304   QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
00305   if ( vlayer && vlayer->providerType() == "spatialite" )
00306   {
00307     QgsDataSourceURI uri( src );
00308     QString database = QgsProject::instance()->writePath( uri.database() );
00309     uri.setConnection( uri.host(), uri.port(), database, uri.username(), uri.password() );
00310     src = uri.uri();
00311   }
00312   else if ( vlayer && vlayer->providerType() == "ogr" )
00313   {
00314     QStringList theURIParts = src.split( "|" );
00315     theURIParts[0] = QgsProject::instance()->writePath( theURIParts[0] );
00316     src = theURIParts.join( "|" );
00317   }
00318   else if ( vlayer && vlayer->providerType() == "delimitedtext" )
00319   {
00320     QUrl urlSource = QUrl::fromEncoded( src.toAscii() );
00321     QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->writePath( urlSource.toLocalFile() ) );
00322     urlDest.setQueryItems( urlSource.queryItems() );
00323     src = QString::fromAscii( urlDest.toEncoded() );
00324   }
00325   else
00326   {
00327     src = QgsProject::instance()->writePath( src );
00328   }
00329 
00330   QDomText dataSourceText = document.createTextNode( src );
00331   dataSource.appendChild( dataSourceText );
00332 
00333   maplayer.appendChild( dataSource );
00334 
00335 
00336   // layer name
00337   QDomElement layerName = document.createElement( "layername" );
00338   QDomText layerNameText = document.createTextNode( name() );
00339   layerName.appendChild( layerNameText );
00340 
00341   maplayer.appendChild( layerName );
00342 
00343   // timestamp if supported
00344   if ( timestamp() > QDateTime() )
00345   {
00346     QDomElement stamp = document.createElement( "timestamp" );
00347     QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
00348     stamp.appendChild( stampText );
00349     maplayer.appendChild( stamp );
00350   }
00351 
00352   maplayer.appendChild( layerName );
00353 
00354   // zorder
00355   // This is no longer stored in the project file. It is superfluous since the layers
00356   // are written and read in the proper order.
00357 
00358   // spatial reference system id
00359   QDomElement mySrsElement = document.createElement( "srs" );
00360   mCRS->writeXML( mySrsElement, document );
00361   maplayer.appendChild( mySrsElement );
00362 
00363   // <transparencyLevelInt>
00364   QDomElement transparencyLevelIntElement = document.createElement( "transparencyLevelInt" );
00365   QDomText    transparencyLevelIntText    = document.createTextNode( QString::number( getTransparency() ) );
00366   transparencyLevelIntElement.appendChild( transparencyLevelIntText );
00367   maplayer.appendChild( transparencyLevelIntElement );
00368   // now append layer node to map layer node
00369 
00370   layer_node.appendChild( maplayer );
00371 
00372   writeCustomProperties( maplayer, document );
00373 
00374   return writeXml( maplayer, document );
00375 
00376 } // bool QgsMapLayer::writeXML
00377 
00378 
00379 
00380 bool QgsMapLayer::writeXml( QDomNode & layer_node, QDomDocument & document )
00381 {
00382   // NOP by default; children will over-ride with behavior specific to them
00383 
00384   return true;
00385 } // void QgsMapLayer::writeXml
00386 
00387 
00388 
00389 
00390 bool QgsMapLayer::isValid()
00391 {
00392   return mValid;
00393 }
00394 
00395 
00396 void QgsMapLayer::invalidTransformInput()
00397 {
00398   QgsDebugMsg( "called" );
00399   // TODO: emit a signal - it will be used to update legend
00400 }
00401 
00402 
00403 QString QgsMapLayer::lastErrorTitle()
00404 {
00405   return QString();
00406 }
00407 
00408 QString QgsMapLayer::lastError()
00409 {
00410   return QString();
00411 }
00412 
00413 void QgsMapLayer::connectNotify( const char * signal )
00414 {
00415   QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
00416 } //  QgsMapLayer::connectNotify
00417 
00418 
00419 
00420 void QgsMapLayer::toggleScaleBasedVisibility( bool theVisibilityFlag )
00421 {
00422   mScaleBasedVisibility = theVisibilityFlag;
00423 }
00424 
00425 bool QgsMapLayer::hasScaleBasedVisibility()
00426 {
00427   return mScaleBasedVisibility;
00428 }
00429 
00430 void QgsMapLayer::setMinimumScale( float theMinScale )
00431 {
00432   mMinScale = theMinScale;
00433 }
00434 
00435 float QgsMapLayer::minimumScale()
00436 {
00437   return mMinScale;
00438 }
00439 
00440 
00441 void QgsMapLayer::setMaximumScale( float theMaxScale )
00442 {
00443   mMaxScale = theMaxScale;
00444 }
00445 
00446 float QgsMapLayer::maximumScale()
00447 {
00448   return mMaxScale;
00449 }
00450 
00451 
00452 QStringList QgsMapLayer::subLayers()
00453 {
00454   return QStringList();  // Empty
00455 }
00456 
00457 void QgsMapLayer::setLayerOrder( QStringList layers )
00458 {
00459   // NOOP
00460 }
00461 
00462 void QgsMapLayer::setSubLayerVisibility( QString name, bool vis )
00463 {
00464   // NOOP
00465 }
00466 
00467 const QgsCoordinateReferenceSystem& QgsMapLayer::crs()
00468 {
00469   return *mCRS;
00470 }
00471 
00472 const QgsCoordinateReferenceSystem& QgsMapLayer::srs()
00473 {
00474   // This will be dropped in QGIS 2.0 due to conflicting name
00475   // Please use crs() in the future
00476   return *mCRS;
00477 }
00478 
00479 void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem& srs, bool emitSignal )
00480 {
00481   *mCRS = srs;
00482   if ( emitSignal )
00483     emit layerCrsChanged();
00484 }
00485 
00486 unsigned int QgsMapLayer::getTransparency()
00487 {
00488   return mTransparencyLevel;
00489 }
00490 
00491 void QgsMapLayer::setTransparency( unsigned int theInt )
00492 {
00493   mTransparencyLevel = theInt;
00494 }
00495 
00496 QString QgsMapLayer::capitaliseLayerName( const QString name )
00497 {
00498   // Capitalise the first letter of the layer name if requested
00499   QSettings settings;
00500   bool capitaliseLayerName =
00501     settings.value( "qgis/capitaliseLayerName", QVariant( false ) ).toBool();
00502 
00503   QString layerName( name );
00504 
00505   if ( capitaliseLayerName )
00506     layerName = layerName.left( 1 ).toUpper() + layerName.mid( 1 );
00507 
00508   return layerName;
00509 }
00510 
00511 QString QgsMapLayer::loadDefaultStyle( bool & theResultFlag )
00512 {
00513   QString myURI = publicSource();
00514   QFileInfo myFileInfo( myURI );
00515   QString key;
00516   if ( myFileInfo.exists() )
00517   {
00518     // get the file name for our .qml style file
00519     key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
00520   }
00521   else
00522   {
00523     key = myURI;
00524   }
00525   return loadNamedStyle( key, theResultFlag );
00526 }
00527 
00528 bool QgsMapLayer::loadNamedStyleFromDb( const QString db, const QString theURI, QString &qml )
00529 {
00530   bool theResultFlag = false;
00531 
00532   // read from database
00533   sqlite3 *myDatabase;
00534   sqlite3_stmt *myPreparedStatement;
00535   const char *myTail;
00536   int myResult;
00537 
00538   QgsDebugMsg( QString( "Trying to load style for \"%1\" from \"%2\"" ).arg( theURI ).arg( db ) );
00539 
00540   if ( !QFile( db ).exists() )
00541     return false;
00542 
00543   myResult = sqlite3_open( db.toUtf8().data(), &myDatabase );
00544   if ( myResult != SQLITE_OK )
00545   {
00546     return false;
00547   }
00548 
00549   QString mySql = "select qml from tbl_styles where style=?";
00550   myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
00551   if ( myResult == SQLITE_OK )
00552   {
00553     QByteArray param = theURI.toUtf8();
00554 
00555     if ( sqlite3_bind_text( myPreparedStatement, 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
00556          sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
00557     {
00558       qml = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
00559       theResultFlag = true;
00560     }
00561 
00562     sqlite3_finalize( myPreparedStatement );
00563   }
00564 
00565   sqlite3_close( myDatabase );
00566 
00567   return theResultFlag;
00568 }
00569 
00570 QString QgsMapLayer::loadNamedStyle( const QString theURI, bool &theResultFlag )
00571 {
00572   theResultFlag = false;
00573 
00574   QDomDocument myDocument( "qgis" );
00575 
00576   // location of problem associated with errorMsg
00577   int line, column;
00578   QString myErrorMessage;
00579 
00580   QFile myFile( theURI );
00581   if ( myFile.open( QFile::ReadOnly ) )
00582   {
00583     // read file
00584     theResultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
00585     if ( !theResultFlag )
00586       myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
00587     myFile.close();
00588   }
00589   else
00590   {
00591     QFileInfo project( QgsProject::instance()->fileName() );
00592     QgsDebugMsg( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ) );
00593 
00594     QString qml;
00595     if ( loadNamedStyleFromDb( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ), theURI, qml ) ||
00596          ( project.exists() && loadNamedStyleFromDb( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), theURI, qml ) ) ||
00597          loadNamedStyleFromDb( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( "resources/qgis.qmldb" ), theURI, qml ) )
00598     {
00599       theResultFlag = myDocument.setContent( qml, &myErrorMessage, &line, &column );
00600       if ( !theResultFlag )
00601       {
00602         myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
00603       }
00604     }
00605     else
00606     {
00607       myErrorMessage = tr( "style not found in database" );
00608     }
00609   }
00610 
00611   if ( !theResultFlag )
00612   {
00613     return myErrorMessage;
00614   }
00615 
00616   // now get the layer node out and pass it over to the layer
00617   // to deserialise...
00618   QDomElement myRoot = myDocument.firstChildElement( "qgis" );
00619   if ( myRoot.isNull() )
00620   {
00621     myErrorMessage = tr( "Error: qgis element could not be found in %1" ).arg( theURI );
00622     theResultFlag = false;
00623     return myErrorMessage;
00624   }
00625 
00626   // use scale dependent visibility flag
00627   toggleScaleBasedVisibility( myRoot.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
00628   setMinimumScale( myRoot.attribute( "minimumScale" ).toFloat() );
00629   setMaximumScale( myRoot.attribute( "maximumScale" ).toFloat() );
00630 
00631   //read transparency level
00632   QDomNode transparencyNode = myRoot.namedItem( "transparencyLevelInt" );
00633   if ( ! transparencyNode.isNull() )
00634   {
00635     // set transparency level only if it's in project
00636     // (otherwise it sets the layer transparent)
00637     QDomElement myElement = transparencyNode.toElement();
00638     setTransparency( myElement.text().toInt() );
00639   }
00640 
00641   QString errorMsg;
00642   theResultFlag = readSymbology( myRoot, errorMsg );
00643   if ( !theResultFlag )
00644   {
00645     myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( theURI ).arg( errorMsg );
00646     return myErrorMessage;
00647   }
00648 
00649   return "";
00650 }
00651 
00652 QString QgsMapLayer::saveDefaultStyle( bool & theResultFlag )
00653 {
00654   return saveNamedStyle( publicSource(), theResultFlag );
00655 }
00656 
00657 QString QgsMapLayer::saveNamedStyle( const QString theURI, bool & theResultFlag )
00658 {
00659   QString myErrorMessage;
00660 
00661   QDomImplementation DomImplementation;
00662   QDomDocumentType documentType =
00663     DomImplementation.createDocumentType(
00664       "qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" );
00665   QDomDocument myDocument( documentType );
00666   QDomElement myRootNode = myDocument.createElement( "qgis" );
00667   myRootNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
00668   myDocument.appendChild( myRootNode );
00669 
00670   // use scale dependent visibility flag
00671   myRootNode.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
00672   myRootNode.setAttribute( "minimumScale", minimumScale() );
00673   myRootNode.setAttribute( "maximumScale", maximumScale() );
00674 
00675   // <transparencyLevelInt>
00676   QDomElement transparencyLevelIntElement = myDocument.createElement( "transparencyLevelInt" );
00677   QDomText    transparencyLevelIntText    = myDocument.createTextNode( QString::number( getTransparency() ) );
00678   transparencyLevelIntElement.appendChild( transparencyLevelIntText );
00679   myRootNode.appendChild( transparencyLevelIntElement );
00680   // now append layer node to map layer node
00681 
00682   QString errorMsg;
00683   if ( !writeSymbology( myRootNode, myDocument, errorMsg ) )
00684   {
00685     return tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
00686   }
00687 
00688   // check if the uri is a file or ends with .qml,
00689   // which indicates that it should become one
00690   // everything else goes to the database
00691   QString filename;
00692 
00693   QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
00694   if ( vlayer && vlayer->providerType() == "ogr" )
00695   {
00696     QStringList theURIParts = theURI.split( "|" );
00697     filename = theURIParts[0];
00698   }
00699   else if ( vlayer && vlayer->providerType() == "delimitedtext" )
00700   {
00701     filename = QUrl::fromEncoded( theURI.toAscii() ).toLocalFile();
00702   }
00703   else
00704   {
00705     filename = theURI;
00706   }
00707 
00708   QFileInfo myFileInfo( filename );
00709   if ( myFileInfo.exists() || filename.endsWith( ".qml", Qt::CaseInsensitive ) )
00710   {
00711     QFileInfo myDirInfo( myFileInfo.path() );  //excludes file name
00712     if ( !myDirInfo.isWritable() )
00713     {
00714       return tr( "The directory containing your dataset needs to be writeable!" );
00715     }
00716 
00717     // now construct the file name for our .qml style file
00718     QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
00719 
00720     QFile myFile( myFileName );
00721     if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
00722     {
00723       QTextStream myFileStream( &myFile );
00724       // save as utf-8 with 2 spaces for indents
00725       myDocument.save( myFileStream, 2 );
00726       myFile.close();
00727       theResultFlag = true;
00728       return tr( "Created default style file as %1" ).arg( myFileName );
00729     }
00730     else
00731     {
00732       theResultFlag = false;
00733       return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
00734     }
00735   }
00736   else
00737   {
00738     QString qml = myDocument.toString();
00739 
00740     // read from database
00741     sqlite3 *myDatabase;
00742     sqlite3_stmt *myPreparedStatement;
00743     const char *myTail;
00744     int myResult;
00745 
00746     myResult = sqlite3_open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ).toUtf8().data(), &myDatabase );
00747     if ( myResult != SQLITE_OK )
00748     {
00749       return tr( "User database could not be opened." );
00750     }
00751 
00752     QByteArray param0 = theURI.toUtf8();
00753     QByteArray param1 = qml.toUtf8();
00754 
00755     QString mySql = "create table if not exists tbl_styles(style varchar primary key,qml varchar)";
00756     myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
00757     if ( myResult == SQLITE_OK )
00758     {
00759       if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
00760       {
00761         sqlite3_finalize( myPreparedStatement );
00762         sqlite3_close( myDatabase );
00763         theResultFlag = false;
00764         return tr( "The style table could not be created." );
00765       }
00766     }
00767 
00768     sqlite3_finalize( myPreparedStatement );
00769 
00770     mySql = "insert into tbl_styles(style,qml) values (?,?)";
00771     myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
00772     if ( myResult == SQLITE_OK )
00773     {
00774       if ( sqlite3_bind_text( myPreparedStatement, 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
00775            sqlite3_bind_text( myPreparedStatement, 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
00776            sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
00777       {
00778         theResultFlag = true;
00779         myErrorMessage = tr( "The style %1 was saved to database" ).arg( theURI );
00780       }
00781     }
00782 
00783     sqlite3_finalize( myPreparedStatement );
00784 
00785     if ( !theResultFlag )
00786     {
00787       QString mySql = "update tbl_styles set qml=? where style=?";
00788       myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
00789       if ( myResult == SQLITE_OK )
00790       {
00791         if ( sqlite3_bind_text( myPreparedStatement, 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
00792              sqlite3_bind_text( myPreparedStatement, 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
00793              sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
00794         {
00795           theResultFlag = true;
00796           myErrorMessage = tr( "The style %1 was updated in the database." ).arg( theURI );
00797         }
00798         else
00799         {
00800           theResultFlag = false;
00801           myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( theURI );
00802         }
00803       }
00804       else
00805       {
00806         theResultFlag = false;
00807         myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( theURI );
00808       }
00809 
00810       sqlite3_finalize( myPreparedStatement );
00811     }
00812 
00813     sqlite3_close( myDatabase );
00814   }
00815 
00816   return myErrorMessage;
00817 }
00818 
00819 
00820 
00821 
00822 QUndoStack* QgsMapLayer::undoStack()
00823 {
00824   return &mUndoStack;
00825 }
00826 
00827 
00828 void QgsMapLayer::setCustomProperty( const QString& key, const QVariant& value )
00829 {
00830   mCustomProperties[key] = value;
00831 }
00832 
00833 QVariant QgsMapLayer::customProperty( const QString& value, const QVariant& defaultValue ) const
00834 {
00835   return mCustomProperties.value( value, defaultValue );
00836 }
00837 
00838 void QgsMapLayer::removeCustomProperty( const QString& key )
00839 {
00840   mCustomProperties.remove( key );
00841 }
00842 
00843 void QgsMapLayer::readCustomProperties( const QDomNode& layerNode, const QString& keyStartsWith )
00844 {
00845   QDomNode propsNode = layerNode.namedItem( "customproperties" );
00846   if ( propsNode.isNull() ) // no properties stored...
00847     return;
00848 
00849   if ( !keyStartsWith.isEmpty() )
00850   {
00851     //remove old keys
00852     QStringList keysToRemove;
00853     QMap<QString, QVariant>::const_iterator pIt = mCustomProperties.constBegin();
00854     for ( ; pIt != mCustomProperties.constEnd(); ++pIt )
00855     {
00856       if ( pIt.key().startsWith( keyStartsWith ) )
00857       {
00858         keysToRemove.push_back( pIt.key() );
00859       }
00860     }
00861 
00862     QStringList::const_iterator sIt = keysToRemove.constBegin();
00863     for ( ; sIt != keysToRemove.constEnd(); ++sIt )
00864     {
00865       mCustomProperties.remove( *sIt );
00866     }
00867   }
00868   else
00869   {
00870     mCustomProperties.clear();
00871   }
00872 
00873   QDomNodeList nodes = propsNode.childNodes();
00874 
00875   for ( int i = 0; i < nodes.size(); i++ )
00876   {
00877     QDomNode propNode = nodes.at( i );
00878     if ( propNode.isNull() || propNode.nodeName() != "property" )
00879       continue;
00880     QDomElement propElement = propNode.toElement();
00881 
00882     QString key = propElement.attribute( "key" );
00883     if ( key.isEmpty() || key.startsWith( keyStartsWith ) )
00884     {
00885       QString value = propElement.attribute( "value" );
00886       mCustomProperties[key] = QVariant( value );
00887     }
00888   }
00889 
00890 }
00891 
00892 void QgsMapLayer::writeCustomProperties( QDomNode & layerNode, QDomDocument & doc ) const
00893 {
00894   //remove already existing <customproperties> tags
00895   QDomNodeList propertyList = layerNode.toElement().elementsByTagName( "customproperties" );
00896   for ( int i = 0; i < propertyList.size(); ++i )
00897   {
00898     layerNode.removeChild( propertyList.at( i ) );
00899   }
00900 
00901   QDomElement propsElement = doc.createElement( "customproperties" );
00902 
00903   for ( QMap<QString, QVariant>::const_iterator it = mCustomProperties.constBegin(); it != mCustomProperties.constEnd(); ++it )
00904   {
00905     QDomElement propElement = doc.createElement( "property" );
00906     propElement.setAttribute( "key", it.key() );
00907     propElement.setAttribute( "value", it.value().toString() );
00908     propsElement.appendChild( propElement );
00909   }
00910 
00911   layerNode.appendChild( propsElement );
00912 }
00913 
00914 void QgsMapLayer::setCacheImage( QImage * thepImage )
00915 {
00916   QgsDebugMsg( "cache Image set!" );
00917   if ( mpCacheImage == thepImage )
00918     return;
00919 
00920   if ( mpCacheImage )
00921   {
00922     delete mpCacheImage;
00923   }
00924   mpCacheImage = thepImage;
00925 }
00926 
00927 bool QgsMapLayer::isEditable() const
00928 {
00929   return false;
00930 }
00931 
00932 void QgsMapLayer::setValid( bool valid )
00933 {
00934   mValid = valid;
00935 }
00936 
00937 void QgsMapLayer::clearCacheImage()
00938 {
00939   setCacheImage( 0 );
00940 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines