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