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