Quantum GIS API Documentation
1.7.4
|
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 }