QGIS API Documentation  2.0.1-Dufour
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsmaplayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaplayer.cpp - description
3  -------------------
4  begin : Fri Jun 28 2002
5  copyright : (C) 2002 by Gary E.Sherman
6  email : sherman at mrcc.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 
19 #include <QDateTime>
20 #include <QDomNode>
21 #include <QFileInfo>
22 #include <QSettings> // TODO: get rid of it [MD]
23 #include <QDir>
24 #include <QFile>
25 #include <QDomDocument>
26 #include <QDomElement>
27 #include <QDomImplementation>
28 #include <QTextStream>
29 #include <QUrl>
30 
31 #include <sqlite3.h>
32 
33 #include "qgslogger.h"
34 #include "qgsrectangle.h"
35 #include "qgsmaplayer.h"
37 #include "qgsapplication.h"
38 #include "qgsproject.h"
40 #include "qgsdatasourceuri.h"
41 #include "qgsvectorlayer.h"
42 #include "qgsproviderregistry.h"
43 
45  QString lyrname,
46  QString source ) :
47  mTransparencyLevel( 255 ), // 0 is completely transparent
48  mValid( false ), // assume the layer is invalid
49  mDataSource( source ),
50  mLayerOrigName( lyrname ), // store the original name
51  mID( "" ),
52  mLayerType( type ),
53  mBlendMode( QPainter::CompositionMode_SourceOver ) // Default to normal blending
54 {
56 
57  // Set the display name = internal name
58  QgsDebugMsg( "original name: '" + mLayerOrigName + "'" );
60  QgsDebugMsg( "display name: '" + mLayerName + "'" );
61 
62  // Generate the unique ID of this layer
63  QDateTime dt = QDateTime::currentDateTime();
64  mID = lyrname + dt.toString( "yyyyMMddhhmmsszzz" );
65  // Tidy the ID up to avoid characters that may cause problems
66  // elsewhere (e.g in some parts of XML). Replaces every non-word
67  // character (word characters are the alphabet, numbers and
68  // underscore) with an underscore.
69  // Note that the first backslashe in the regular expression is
70  // there for the compiler, so the pattern is actually \W
71  mID.replace( QRegExp( "[\\W]" ), "_" );
72 
73  //set some generous defaults for scale based visibility
74  mMinScale = 0;
75  mMaxScale = 100000000;
76  mScaleBasedVisibility = false;
77  mpCacheImage = 0;
78 }
79 
81 {
82  delete mCRS;
83  if ( mpCacheImage )
84  {
85  delete mpCacheImage;
86  }
87 }
88 
90 {
91  return mLayerType;
92 }
93 
95 QString QgsMapLayer::id() const
96 {
97  return mID;
98 }
99 
101 void QgsMapLayer::setLayerName( const QString & name )
102 {
103  QgsDebugMsg( "new original name: '" + name + "'" );
104  QString newName = capitaliseLayerName( name );
105  QgsDebugMsg( "new display name: '" + name + "'" );
106  if ( name == mLayerOrigName && newName == mLayerName ) return;
107  mLayerOrigName = name; // store the new original name
108  mLayerName = newName;
109  emit layerNameChanged();
110 }
111 
113 QString const & QgsMapLayer::name() const
114 {
115  QgsDebugMsgLevel( "returning name '" + mLayerName + "'", 3 );
116  return mLayerName;
117 }
118 
120 {
121  // Redo this every time we're asked for it, as we don't know if
122  // dataSource has changed.
123  QString safeName = QgsDataSourceURI::removePassword( mDataSource );
124  return safeName;
125 }
126 
127 QString const & QgsMapLayer::source() const
128 {
129  return mDataSource;
130 }
131 
133 {
134  return mExtent;
135 }
136 
138 void QgsMapLayer::setBlendMode( const QPainter::CompositionMode blendMode )
139 {
141 }
142 
144 QPainter::CompositionMode QgsMapLayer::blendMode() const
145 {
146  return mBlendMode;
147 }
148 
149 bool QgsMapLayer::draw( QgsRenderContext& rendererContext )
150 {
151  Q_UNUSED( rendererContext );
152  return false;
153 }
154 
156 {
157  Q_UNUSED( rendererContext );
158 }
159 
160 bool QgsMapLayer::readLayerXML( const QDomElement& layerElement )
161 {
163  CUSTOM_CRS_VALIDATION savedValidation;
164  bool layerError;
165 
166  QDomNode mnl;
167  QDomElement mne;
168 
169  // read provider
170  QString provider;
171  mnl = layerElement.namedItem( "provider" );
172  mne = mnl.toElement();
173  provider = mne.text();
174 
175  // set data source
176  mnl = layerElement.namedItem( "datasource" );
177  mne = mnl.toElement();
178  mDataSource = mne.text();
179 
180  // TODO: this should go to providers
181  if ( provider == "spatialite" )
182  {
184  uri.setDatabase( QgsProject::instance()->readPath( uri.database() ) );
185  mDataSource = uri.uri();
186  }
187  else if ( provider == "ogr" )
188  {
189  QStringList theURIParts = mDataSource.split( "|" );
190  theURIParts[0] = QgsProject::instance()->readPath( theURIParts[0] );
191  mDataSource = theURIParts.join( "|" );
192  }
193  else if ( provider == "delimitedtext" )
194  {
195  QUrl urlSource = QUrl::fromEncoded( mDataSource.toAscii() );
196 
197  if ( !mDataSource.startsWith( "file:" ) )
198  {
199  QUrl file = QUrl::fromLocalFile( mDataSource.left( mDataSource.indexOf( "?" ) ) );
200  urlSource.setScheme( "file" );
201  urlSource.setPath( file.path() );
202  }
203 
204  QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->readPath( urlSource.toLocalFile() ) );
205  urlDest.setQueryItems( urlSource.queryItems() );
206  mDataSource = QString::fromAscii( urlDest.toEncoded() );
207  }
208  else if ( provider == "wms" )
209  {
210  // >>> BACKWARD COMPATIBILITY < 1.9
211  // For project file backward compatibility we must support old format:
212  // 1. mode: <url>
213  // example: http://example.org/wms?
214  // 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
215  // example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
216  // example: featureCount=10,http://example.org/wms?
217  // example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
218  // This is modified version of old QgsWmsProvider::parseUri
219  // The new format has always params crs,format,layers,styles and that params
220  // should not appear in old format url -> use them to identify version
221  if ( !mDataSource.contains( "crs=" ) && !mDataSource.contains( "format=" ) )
222  {
223  QgsDebugMsg( "Old WMS URI format detected -> converting to new format" );
224  QgsDataSourceURI uri;
225  if ( !mDataSource.startsWith( "http:" ) )
226  {
227  QStringList parts = mDataSource.split( "," );
228  QStringListIterator iter( parts );
229  while ( iter.hasNext() )
230  {
231  QString item = iter.next();
232  if ( item.startsWith( "username=" ) )
233  {
234  uri.setParam( "username", item.mid( 9 ) );
235  }
236  else if ( item.startsWith( "password=" ) )
237  {
238  uri.setParam( "password", item.mid( 9 ) );
239  }
240  else if ( item.startsWith( "tiled=" ) )
241  {
242  // in < 1.9 tiled= may apper in to variants:
243  // tiled=width;height - non tiled mode, specifies max width and max height
244  // tiled=width;height;resolutions-1;resolution2;... - tile mode
245 
246  QStringList params = item.mid( 6 ).split( ";" );
247 
248  if ( params.size() == 2 ) // non tiled mode
249  {
250  uri.setParam( "maxWidth", params.takeFirst() );
251  uri.setParam( "maxHeight", params.takeFirst() );
252  }
253  else if ( params.size() > 2 ) // tiled mode
254  {
255  // resolutions are no more needed and size limit is not used for tiles
256  // we have to tell to the provider however that it is tiled
257  uri.setParam( "tileMatrixSet", "" );
258  }
259  }
260  else if ( item.startsWith( "featureCount=" ) )
261  {
262  uri.setParam( "featureCount", item.mid( 13 ) );
263  }
264  else if ( item.startsWith( "url=" ) )
265  {
266  uri.setParam( "url", item.mid( 4 ) );
267  }
268  else if ( item.startsWith( "ignoreUrl=" ) )
269  {
270  uri.setParam( "ignoreUrl", item.mid( 10 ).split( ";" ) );
271  }
272  }
273  }
274  else
275  {
276  uri.setParam( "url", mDataSource );
277  }
278  mDataSource = uri.encodedUri();
279  // At this point, the URI is obviously incomplete, we add additional params
280  // in QgsRasterLayer::readXml
281  }
282  // <<< BACKWARD COMPATIBILITY < 1.9
283  }
284  else
285  {
287  }
288 
289  // Set the CRS from project file, asking the user if necessary.
290  // Make it the saved CRS to have WMS layer projected correctly.
291  // We will still overwrite whatever GDAL etc picks up anyway
292  // further down this function.
293  mnl = layerElement.namedItem( "layername" );
294  mne = mnl.toElement();
295 
296  QDomNode srsNode = layerElement.namedItem( "srs" );
297  mCRS->readXML( srsNode );
298  mCRS->setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
299  mCRS->validate();
300  savedCRS = *mCRS;
301 
302  // Do not validate any projections in children, they will be overwritten anyway.
303  // No need to ask the user for a projections when it is overwritten, is there?
306 
307  // now let the children grab what they need from the Dom node.
308  layerError = !readXml( layerElement );
309 
310  // overwrite CRS with what we read from project file before the raster/vector
311  // file readnig functions changed it. They will if projections is specfied in the file.
312  // FIXME: is this necessary?
314  *mCRS = savedCRS;
315 
316  // Abort if any error in layer, such as not found.
317  if ( layerError )
318  {
319  return false;
320  }
321 
322  // the internal name is just the data source basename
323  //QFileInfo dataSourceFileInfo( mDataSource );
324  //internalName = dataSourceFileInfo.baseName();
325 
326  // set ID
327  mnl = layerElement.namedItem( "id" );
328  if ( ! mnl.isNull() )
329  {
330  mne = mnl.toElement();
331  if ( ! mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
332  {
333  mID = mne.text();
334  }
335  }
336 
337  // use scale dependent visibility flag
338  toggleScaleBasedVisibility( layerElement.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
339  setMinimumScale( layerElement.attribute( "minimumScale" ).toFloat() );
340  setMaximumScale( layerElement.attribute( "maximumScale" ).toFloat() );
341 
342  // set name
343  mnl = layerElement.namedItem( "layername" );
344  mne = mnl.toElement();
345  setLayerName( mne.text() );
346 
347  //title
348  QDomElement titleElem = layerElement.firstChildElement( "title" );
349  if ( !titleElem.isNull() )
350  {
351  mTitle = titleElem.text();
352  }
353 
354  //abstract
355  QDomElement abstractElem = layerElement.firstChildElement( "abstract" );
356  if ( !abstractElem.isNull() )
357  {
358  mAbstract = abstractElem.text();
359  }
360 
361  //keywordList
362  QDomElement keywordListElem = layerElement.firstChildElement( "keywordList" );
363  if ( !keywordListElem.isNull() )
364  {
365  QStringList kwdList;
366  for ( QDomNode n = keywordListElem.firstChild(); !n.isNull(); n = n.nextSibling() )
367  {
368  kwdList << n.toElement().text();
369  }
370  mKeywordList = kwdList.join( ", " );
371  }
372 
373  //metadataUrl
374  QDomElement dataUrlElem = layerElement.firstChildElement( "dataUrl" );
375  if ( !dataUrlElem.isNull() )
376  {
377  mDataUrl = dataUrlElem.text();
378  mDataUrlFormat = dataUrlElem.attribute( "format", "" );
379  }
380 
381  //attribution
382  QDomElement attribElem = layerElement.firstChildElement( "attribution" );
383  if ( !attribElem.isNull() )
384  {
385  mAttribution = attribElem.text();
386  mAttributionUrl = attribElem.attribute( "href", "" );
387  }
388 
389  //metadataUrl
390  QDomElement metaUrlElem = layerElement.firstChildElement( "metadataUrl" );
391  if ( !metaUrlElem.isNull() )
392  {
393  mMetadataUrl = metaUrlElem.text();
394  mMetadataUrlType = metaUrlElem.attribute( "type", "" );
395  mMetadataUrlFormat = metaUrlElem.attribute( "format", "" );
396  }
397 
398 #if 0
399  //read transparency level
400  QDomNode transparencyNode = layer_node.namedItem( "transparencyLevelInt" );
401  if ( ! transparencyNode.isNull() )
402  {
403  // set transparency level only if it's in project
404  // (otherwise it sets the layer transparent)
405  QDomElement myElement = transparencyNode.toElement();
406  setTransparency( myElement.text().toInt() );
407  }
408 #endif
409 
410  readCustomProperties( layerElement );
411 
412  return true;
413 } // bool QgsMapLayer::readLayerXML
414 
415 
416 bool QgsMapLayer::readXml( const QDomNode& layer_node )
417 {
418  Q_UNUSED( layer_node );
419  // NOP by default; children will over-ride with behavior specific to them
420 
421  return true;
422 } // void QgsMapLayer::readXml
423 
424 
425 
426 bool QgsMapLayer::writeLayerXML( QDomElement& layerElement, QDomDocument& document )
427 {
428  // use scale dependent visibility flag
429  layerElement.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
430  layerElement.setAttribute( "minimumScale", QString::number( minimumScale() ) );
431  layerElement.setAttribute( "maximumScale", QString::number( maximumScale() ) );
432 
433  // ID
434  QDomElement layerId = document.createElement( "id" );
435  QDomText layerIdText = document.createTextNode( id() );
436  layerId.appendChild( layerIdText );
437 
438  layerElement.appendChild( layerId );
439 
440  // data source
441  QDomElement dataSource = document.createElement( "datasource" );
442 
443  QString src = source();
444 
445  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
446  // TODO: what about postgres, mysql and others, they should not go through writePath()
447  if ( vlayer && vlayer->providerType() == "spatialite" )
448  {
449  QgsDataSourceURI uri( src );
450  QString database = QgsProject::instance()->writePath( uri.database() );
451  uri.setConnection( uri.host(), uri.port(), database, uri.username(), uri.password() );
452  src = uri.uri();
453  }
454  else if ( vlayer && vlayer->providerType() == "ogr" )
455  {
456  QStringList theURIParts = src.split( "|" );
457  theURIParts[0] = QgsProject::instance()->writePath( theURIParts[0] );
458  src = theURIParts.join( "|" );
459  }
460  else if ( vlayer && vlayer->providerType() == "delimitedtext" )
461  {
462  QUrl urlSource = QUrl::fromEncoded( src.toAscii() );
463  QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->writePath( urlSource.toLocalFile() ) );
464  urlDest.setQueryItems( urlSource.queryItems() );
465  src = QString::fromAscii( urlDest.toEncoded() );
466  }
467  else
468  {
469  src = QgsProject::instance()->writePath( src );
470  }
471 
472  QDomText dataSourceText = document.createTextNode( src );
473  dataSource.appendChild( dataSourceText );
474 
475  layerElement.appendChild( dataSource );
476 
477 
478  // layer name
479  QDomElement layerName = document.createElement( "layername" );
480  QDomText layerNameText = document.createTextNode( originalName() );
481  layerName.appendChild( layerNameText );
482 
483  // layer title
484  QDomElement layerTitle = document.createElement( "title" ) ;
485  QDomText layerTitleText = document.createTextNode( title() );
486  layerTitle.appendChild( layerTitleText );
487 
488  // layer abstract
489  QDomElement layerAbstract = document.createElement( "abstract" );
490  QDomText layerAbstractText = document.createTextNode( abstract() );
491  layerAbstract.appendChild( layerAbstractText );
492 
493  layerElement.appendChild( layerName );
494  layerElement.appendChild( layerTitle );
495  layerElement.appendChild( layerAbstract );
496 
497  // layer keyword list
498  QStringList keywordStringList = keywordList().split( "," );
499  if ( keywordStringList.size() > 0 )
500  {
501  QDomElement layerKeywordList = document.createElement( "keywordList" );
502  for ( int i = 0; i < keywordStringList.size(); ++i )
503  {
504  QDomElement layerKeywordValue = document.createElement( "value" );
505  QDomText layerKeywordText = document.createTextNode( keywordStringList.at( i ).trimmed() );
506  layerKeywordValue.appendChild( layerKeywordText );
507  layerKeywordList.appendChild( layerKeywordValue );
508  }
509  layerElement.appendChild( layerKeywordList );
510  }
511 
512  // layer metadataUrl
513  QString aDataUrl = dataUrl();
514  if ( !aDataUrl.isEmpty() )
515  {
516  QDomElement layerDataUrl = document.createElement( "dataUrl" ) ;
517  QDomText layerDataUrlText = document.createTextNode( aDataUrl );
518  layerDataUrl.appendChild( layerDataUrlText );
519  layerDataUrl.setAttribute( "format", dataUrlFormat() );
520  layerElement.appendChild( layerDataUrl );
521  }
522 
523  // layer attribution
524  QString aAttribution = attribution();
525  if ( !aAttribution.isEmpty() )
526  {
527  QDomElement layerAttribution = document.createElement( "attribution" ) ;
528  QDomText layerAttributionText = document.createTextNode( aAttribution );
529  layerAttribution.appendChild( layerAttributionText );
530  layerAttribution.setAttribute( "href", attributionUrl() );
531  layerElement.appendChild( layerAttribution );
532  }
533 
534  // layer metadataUrl
535  QString aMetadataUrl = metadataUrl();
536  if ( !aMetadataUrl.isEmpty() )
537  {
538  QDomElement layerMetadataUrl = document.createElement( "metadataUrl" ) ;
539  QDomText layerMetadataUrlText = document.createTextNode( aMetadataUrl );
540  layerMetadataUrl.appendChild( layerMetadataUrlText );
541  layerMetadataUrl.setAttribute( "type", metadataUrlType() );
542  layerMetadataUrl.setAttribute( "format", metadataUrlFormat() );
543  layerElement.appendChild( layerMetadataUrl );
544  }
545 
546  // timestamp if supported
547  if ( timestamp() > QDateTime() )
548  {
549  QDomElement stamp = document.createElement( "timestamp" );
550  QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
551  stamp.appendChild( stampText );
552  layerElement.appendChild( stamp );
553  }
554 
555  layerElement.appendChild( layerName );
556 
557  // zorder
558  // This is no longer stored in the project file. It is superfluous since the layers
559  // are written and read in the proper order.
560 
561  // spatial reference system id
562  QDomElement mySrsElement = document.createElement( "srs" );
563  mCRS->writeXML( mySrsElement, document );
564  layerElement.appendChild( mySrsElement );
565 
566 #if 0
567  // <transparencyLevelInt>
568  QDomElement transparencyLevelIntElement = document.createElement( "transparencyLevelInt" );
569  QDomText transparencyLevelIntText = document.createTextNode( QString::number( getTransparency() ) );
570  transparencyLevelIntElement.appendChild( transparencyLevelIntText );
571  maplayer.appendChild( transparencyLevelIntElement );
572 #endif
573 
574  // now append layer node to map layer node
575 
576  writeCustomProperties( layerElement, document );
577 
578  return writeXml( layerElement, document );
579 
580 } // bool QgsMapLayer::writeXML
581 
582 
583 bool QgsMapLayer::writeXml( QDomNode & layer_node, QDomDocument & document )
584 {
585  Q_UNUSED( layer_node );
586  Q_UNUSED( document );
587  // NOP by default; children will over-ride with behavior specific to them
588 
589  return true;
590 } // void QgsMapLayer::writeXml
591 
592 
593 
594 
596 {
597  return mValid;
598 }
599 
600 
602 {
603  QgsDebugMsg( "called" );
604  // TODO: emit a signal - it will be used to update legend
605 }
606 
607 
609 {
610  return QString();
611 }
612 
614 {
615  return QString();
616 }
617 
618 void QgsMapLayer::connectNotify( const char * signal )
619 {
620  Q_UNUSED( signal );
621  QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
622 } // QgsMapLayer::connectNotify
623 
624 
625 
626 void QgsMapLayer::toggleScaleBasedVisibility( bool theVisibilityFlag )
627 {
628  mScaleBasedVisibility = theVisibilityFlag;
629 }
630 
632 {
633  return mScaleBasedVisibility;
634 }
635 
636 void QgsMapLayer::setMinimumScale( float theMinScale )
637 {
638  mMinScale = theMinScale;
639 }
640 
642 {
643  return mMinScale;
644 }
645 
646 
647 void QgsMapLayer::setMaximumScale( float theMaxScale )
648 {
649  mMaxScale = theMaxScale;
650 }
651 
653 {
654  return mMaxScale;
655 }
656 
657 
658 QStringList QgsMapLayer::subLayers() const
659 {
660  return QStringList(); // Empty
661 }
662 
663 void QgsMapLayer::setLayerOrder( const QStringList &layers )
664 {
665  Q_UNUSED( layers );
666  // NOOP
667 }
668 
669 void QgsMapLayer::setSubLayerVisibility( QString name, bool vis )
670 {
671  Q_UNUSED( name );
672  Q_UNUSED( vis );
673  // NOOP
674 }
675 
677 {
678  return *mCRS;
679 }
680 
681 void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem& srs, bool emitSignal )
682 {
683  *mCRS = srs;
684 
685  if ( !mCRS->isValid() )
686  {
687  mCRS->setValidationHint( tr( "Specify CRS for layer %1" ).arg( name() ) );
688  mCRS->validate();
689  }
690 
691  if ( emitSignal )
692  emit layerCrsChanged();
693 }
694 
695 #if 0
696 unsigned int QgsMapLayer::getTransparency()
697 {
698  return mTransparencyLevel;
699 }
700 
701 void QgsMapLayer::setTransparency( unsigned int theInt )
702 {
703  mTransparencyLevel = theInt;
704 }
705 #endif
706 
707 QString QgsMapLayer::capitaliseLayerName( const QString& name )
708 {
709  // Capitalise the first letter of the layer name if requested
710  QSettings settings;
711  bool capitaliseLayerName =
712  settings.value( "/qgis/capitaliseLayerName", QVariant( false ) ).toBool();
713 
714  QString layerName( name );
715 
716  if ( capitaliseLayerName )
717  layerName = layerName.left( 1 ).toUpper() + layerName.mid( 1 );
718 
719  return layerName;
720 }
721 
723 {
724  QString myURI = publicSource();
725 
726  // if file is using the VSIFILE mechanism, remove the prefix
727  if ( myURI.startsWith( "/vsigzip/", Qt::CaseInsensitive ) )
728  {
729  myURI.remove( 0, 9 );
730  }
731  else if ( myURI.startsWith( "/vsizip/", Qt::CaseInsensitive ) &&
732  myURI.endsWith( ".zip", Qt::CaseInsensitive ) )
733  {
734  // ideally we should look for .qml file inside zip file
735  myURI.remove( 0, 8 );
736  }
737  else if ( myURI.startsWith( "/vsitar/", Qt::CaseInsensitive ) &&
738  ( myURI.endsWith( ".tar", Qt::CaseInsensitive ) ||
739  myURI.endsWith( ".tar.gz", Qt::CaseInsensitive ) ||
740  myURI.endsWith( ".tgz", Qt::CaseInsensitive ) ) )
741  {
742  // ideally we should look for .qml file inside tar file
743  myURI.remove( 0, 8 );
744  }
745 
746  QFileInfo myFileInfo( myURI );
747  QString key;
748 
749  if ( myFileInfo.exists() )
750  {
751  // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name
752  if ( myURI.endsWith( ".gz", Qt::CaseInsensitive ) )
753  myURI.chop( 3 );
754  else if ( myURI.endsWith( ".zip", Qt::CaseInsensitive ) )
755  myURI.chop( 4 );
756  else if ( myURI.endsWith( ".tar", Qt::CaseInsensitive ) )
757  myURI.chop( 4 );
758  else if ( myURI.endsWith( ".tar.gz", Qt::CaseInsensitive ) )
759  myURI.chop( 7 );
760  else if ( myURI.endsWith( ".tgz", Qt::CaseInsensitive ) )
761  myURI.chop( 4 );
762  else if ( myURI.endsWith( ".gz", Qt::CaseInsensitive ) )
763  myURI.chop( 3 );
764  myFileInfo.setFile( myURI );
765  // get the file name for our .qml style file
766  key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
767  }
768  else
769  {
770  key = publicSource();
771  }
772 
773  return key;
774 }
775 
776 QString QgsMapLayer::loadDefaultStyle( bool & theResultFlag )
777 {
778  return loadNamedStyle( styleURI(), theResultFlag );
779 }
780 
781 bool QgsMapLayer::loadNamedStyleFromDb( const QString db, const QString theURI, QString &qml )
782 {
783  QgsDebugMsg( QString( "db = %1 uri = %2" ).arg( db ).arg( theURI ) );
784 
785  bool theResultFlag = false;
786 
787  // read from database
788  sqlite3 *myDatabase;
789  sqlite3_stmt *myPreparedStatement;
790  const char *myTail;
791  int myResult;
792 
793  QgsDebugMsg( QString( "Trying to load style for \"%1\" from \"%2\"" ).arg( theURI ).arg( db ) );
794 
795  if ( !QFile( db ).exists() )
796  return false;
797 
798  myResult = sqlite3_open_v2( db.toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, NULL );
799  if ( myResult != SQLITE_OK )
800  {
801  return false;
802  }
803 
804  QString mySql = "select qml from tbl_styles where style=?";
805  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
806  if ( myResult == SQLITE_OK )
807  {
808  QByteArray param = theURI.toUtf8();
809 
810  if ( sqlite3_bind_text( myPreparedStatement, 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
811  sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
812  {
813  qml = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
814  theResultFlag = true;
815  }
816 
817  sqlite3_finalize( myPreparedStatement );
818  }
819 
820  sqlite3_close( myDatabase );
821 
822  return theResultFlag;
823 }
824 
825 QString QgsMapLayer::loadNamedStyle( const QString theURI, bool &theResultFlag )
826 {
827  QgsDebugMsg( QString( "uri = %1 myURI = %2" ).arg( theURI ).arg( publicSource() ) );
828 
829  theResultFlag = false;
830 
831  QDomDocument myDocument( "qgis" );
832 
833  // location of problem associated with errorMsg
834  int line, column;
835  QString myErrorMessage;
836 
837  QFile myFile( theURI );
838  if ( myFile.open( QFile::ReadOnly ) )
839  {
840  // read file
841  theResultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
842  if ( !theResultFlag )
843  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
844  myFile.close();
845  }
846  else
847  {
848  QFileInfo project( QgsProject::instance()->fileName() );
849  QgsDebugMsg( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ) );
850 
851  QString qml;
852  if ( loadNamedStyleFromDb( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ), theURI, qml ) ||
853  ( project.exists() && loadNamedStyleFromDb( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), theURI, qml ) ) ||
854  loadNamedStyleFromDb( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( "resources/qgis.qmldb" ), theURI, qml ) )
855  {
856  theResultFlag = myDocument.setContent( qml, &myErrorMessage, &line, &column );
857  if ( !theResultFlag )
858  {
859  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
860  }
861  }
862  else
863  {
864  myErrorMessage = tr( "style not found in database" );
865  }
866  }
867 
868  if ( !theResultFlag )
869  {
870  return myErrorMessage;
871  }
872 
873  // get style file version string, if any
874  QgsProjectVersion fileVersion( myDocument.firstChildElement( "qgis" ).attribute( "version" ) );
875  QgsProjectVersion thisVersion( QGis::QGIS_VERSION );
876 
877  if ( thisVersion > fileVersion )
878  {
879  QgsLogger::warning( "Loading a style file that was saved with an older "
880  "version of qgis (saved in " + fileVersion.text() +
881  ", loaded in " + QGis::QGIS_VERSION +
882  "). Problems may occur." );
883 
884  QgsProjectFileTransform styleFile( myDocument, fileVersion );
885  // styleFile.dump();
886  styleFile.updateRevision( thisVersion );
887  // styleFile.dump();
888  }
889 
890  // now get the layer node out and pass it over to the layer
891  // to deserialise...
892  QDomElement myRoot = myDocument.firstChildElement( "qgis" );
893  if ( myRoot.isNull() )
894  {
895  myErrorMessage = tr( "Error: qgis element could not be found in %1" ).arg( theURI );
896  theResultFlag = false;
897  return myErrorMessage;
898  }
899 
900  // use scale dependent visibility flag
901  toggleScaleBasedVisibility( myRoot.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
902  setMinimumScale( myRoot.attribute( "minimumScale" ).toFloat() );
903  setMaximumScale( myRoot.attribute( "maximumScale" ).toFloat() );
904 
905 #if 0
906  //read transparency level
907  QDomNode transparencyNode = myRoot.namedItem( "transparencyLevelInt" );
908  if ( ! transparencyNode.isNull() )
909  {
910  // set transparency level only if it's in project
911  // (otherwise it sets the layer transparent)
912  QDomElement myElement = transparencyNode.toElement();
913  setTransparency( myElement.text().toInt() );
914  }
915 #endif
916 
917  QString errorMsg;
918  theResultFlag = readSymbology( myRoot, errorMsg );
919  if ( !theResultFlag )
920  {
921  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( theURI ).arg( errorMsg );
922  return myErrorMessage;
923  }
924 
925  return "";
926 }
927 
928 void QgsMapLayer::exportNamedStyle( QDomDocument &doc, QString &errorMsg )
929 {
930  QDomImplementation DomImplementation;
931  QDomDocumentType documentType = DomImplementation.createDocumentType( "qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" );
932  QDomDocument myDocument( documentType );
933 
934  QDomElement myRootNode = myDocument.createElement( "qgis" );
935  myRootNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
936  myDocument.appendChild( myRootNode );
937 
938  myRootNode.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
939  myRootNode.setAttribute( "minimumScale", QString::number( minimumScale() ) );
940  myRootNode.setAttribute( "maximumScale", QString::number( maximumScale() ) );
941 
942 #if 0
943  // <transparencyLevelInt>
944  QDomElement transparencyLevelIntElement = myDocument.createElement( "transparencyLevelInt" );
945  QDomText transparencyLevelIntText = myDocument.createTextNode( QString::number( getTransparency() ) );
946  transparencyLevelIntElement.appendChild( transparencyLevelIntText );
947  myRootNode.appendChild( transparencyLevelIntElement );
948 #endif
949 
950  if ( !writeSymbology( myRootNode, myDocument, errorMsg ) )
951  {
952  errorMsg = QObject::tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
953  return;
954  }
955  doc = myDocument;
956 }
957 
958 QString QgsMapLayer::saveDefaultStyle( bool & theResultFlag )
959 {
960  return saveNamedStyle( styleURI(), theResultFlag );
961 }
962 
963 QString QgsMapLayer::saveNamedStyle( const QString theURI, bool & theResultFlag )
964 {
965  QString myErrorMessage;
966  QDomDocument myDocument;
967  exportNamedStyle( myDocument, myErrorMessage );
968 
969  // check if the uri is a file or ends with .qml,
970  // which indicates that it should become one
971  // everything else goes to the database
972  QString filename;
973 
974  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
975  if ( vlayer && vlayer->providerType() == "ogr" )
976  {
977  QStringList theURIParts = theURI.split( "|" );
978  filename = theURIParts[0];
979  }
980  else if ( vlayer && vlayer->providerType() == "delimitedtext" )
981  {
982  filename = QUrl::fromEncoded( theURI.toAscii() ).toLocalFile();
983  }
984  else
985  {
986  filename = theURI;
987  }
988 
989  QFileInfo myFileInfo( filename );
990  if ( myFileInfo.exists() || filename.endsWith( ".qml", Qt::CaseInsensitive ) )
991  {
992  QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
993  if ( !myDirInfo.isWritable() )
994  {
995  return tr( "The directory containing your dataset needs to be writable!" );
996  }
997 
998  // now construct the file name for our .qml style file
999  QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
1000 
1001  QFile myFile( myFileName );
1002  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1003  {
1004  QTextStream myFileStream( &myFile );
1005  // save as utf-8 with 2 spaces for indents
1006  myDocument.save( myFileStream, 2 );
1007  myFile.close();
1008  theResultFlag = true;
1009  return tr( "Created default style file as %1" ).arg( myFileName );
1010  }
1011  else
1012  {
1013  theResultFlag = false;
1014  return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
1015  }
1016  }
1017  else
1018  {
1019  QString qml = myDocument.toString();
1020 
1021  // read from database
1022  sqlite3 *myDatabase;
1023  sqlite3_stmt *myPreparedStatement;
1024  const char *myTail;
1025  int myResult;
1026 
1027  myResult = sqlite3_open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ).toUtf8().data(), &myDatabase );
1028  if ( myResult != SQLITE_OK )
1029  {
1030  return tr( "User database could not be opened." );
1031  }
1032 
1033  QByteArray param0 = theURI.toUtf8();
1034  QByteArray param1 = qml.toUtf8();
1035 
1036  QString mySql = "create table if not exists tbl_styles(style varchar primary key,qml varchar)";
1037  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1038  if ( myResult == SQLITE_OK )
1039  {
1040  if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
1041  {
1042  sqlite3_finalize( myPreparedStatement );
1043  sqlite3_close( myDatabase );
1044  theResultFlag = false;
1045  return tr( "The style table could not be created." );
1046  }
1047  }
1048 
1049  sqlite3_finalize( myPreparedStatement );
1050 
1051  mySql = "insert into tbl_styles(style,qml) values (?,?)";
1052  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1053  if ( myResult == SQLITE_OK )
1054  {
1055  if ( sqlite3_bind_text( myPreparedStatement, 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
1056  sqlite3_bind_text( myPreparedStatement, 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
1057  sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
1058  {
1059  theResultFlag = true;
1060  myErrorMessage = tr( "The style %1 was saved to database" ).arg( theURI );
1061  }
1062  }
1063 
1064  sqlite3_finalize( myPreparedStatement );
1065 
1066  if ( !theResultFlag )
1067  {
1068  QString mySql = "update tbl_styles set qml=? where style=?";
1069  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1070  if ( myResult == SQLITE_OK )
1071  {
1072  if ( sqlite3_bind_text( myPreparedStatement, 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
1073  sqlite3_bind_text( myPreparedStatement, 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
1074  sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
1075  {
1076  theResultFlag = true;
1077  myErrorMessage = tr( "The style %1 was updated in the database." ).arg( theURI );
1078  }
1079  else
1080  {
1081  theResultFlag = false;
1082  myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( theURI );
1083  }
1084  }
1085  else
1086  {
1087  theResultFlag = false;
1088  myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( theURI );
1089  }
1090 
1091  sqlite3_finalize( myPreparedStatement );
1092  }
1093 
1094  sqlite3_close( myDatabase );
1095  }
1096 
1097  return myErrorMessage;
1098 }
1099 
1100 void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg )
1101 {
1102  QDomDocument myDocument = QDomDocument();
1103 
1104  QDomNode header = myDocument.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" );
1105  myDocument.appendChild( header );
1106 
1107  // Create the root element
1108  QDomElement root = myDocument.createElementNS( "http://www.opengis.net/sld", "StyledLayerDescriptor" );
1109  root.setAttribute( "version", "1.1.0" );
1110  root.setAttribute( "xsi:schemaLocation", "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" );
1111  root.setAttribute( "xmlns:ogc", "http://www.opengis.net/ogc" );
1112  root.setAttribute( "xmlns:se", "http://www.opengis.net/se" );
1113  root.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
1114  root.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
1115  myDocument.appendChild( root );
1116 
1117  // Create the NamedLayer element
1118  QDomElement namedLayerNode = myDocument.createElement( "NamedLayer" );
1119  root.appendChild( namedLayerNode );
1120 
1121  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1122  if ( !vlayer )
1123  {
1124  errorMsg = tr( "Could not save symbology because:\n%1" )
1125  .arg( "Non-vector layers not supported yet" );
1126  return;
1127  }
1128 
1129  if ( !vlayer->writeSld( namedLayerNode, myDocument, errorMsg ) )
1130  {
1131  errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1132  return;
1133  }
1134 
1135  doc = myDocument;
1136 }
1137 
1138 QString QgsMapLayer::saveSldStyle( const QString theURI, bool & theResultFlag )
1139 {
1140  QString errorMsg;
1141  QDomDocument myDocument;
1142  exportSldStyle( myDocument, errorMsg );
1143  if ( !errorMsg.isNull() )
1144  {
1145  theResultFlag = false;
1146  return errorMsg;
1147  }
1148  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1149 
1150  // check if the uri is a file or ends with .sld,
1151  // which indicates that it should become one
1152  QString filename;
1153  if ( vlayer->providerType() == "ogr" )
1154  {
1155  QStringList theURIParts = theURI.split( "|" );
1156  filename = theURIParts[0];
1157  }
1158  else if ( vlayer->providerType() == "delimitedtext" )
1159  {
1160  filename = QUrl::fromEncoded( theURI.toAscii() ).toLocalFile();
1161  }
1162  else
1163  {
1164  filename = theURI;
1165  }
1166 
1167  QFileInfo myFileInfo( filename );
1168  if ( myFileInfo.exists() || filename.endsWith( ".sld", Qt::CaseInsensitive ) )
1169  {
1170  QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1171  if ( !myDirInfo.isWritable() )
1172  {
1173  return tr( "The directory containing your dataset needs to be writable!" );
1174  }
1175 
1176  // now construct the file name for our .sld style file
1177  QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".sld";
1178 
1179  QFile myFile( myFileName );
1180  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1181  {
1182  QTextStream myFileStream( &myFile );
1183  // save as utf-8 with 2 spaces for indents
1184  myDocument.save( myFileStream, 2 );
1185  myFile.close();
1186  theResultFlag = true;
1187  return tr( "Created default style file as %1" ).arg( myFileName );
1188  }
1189  }
1190 
1191  theResultFlag = false;
1192  return tr( "ERROR: Failed to created SLD style file as %1. Check file permissions and retry." ).arg( filename );
1193 }
1194 
1195 QString QgsMapLayer::loadSldStyle( const QString theURI, bool &theResultFlag )
1196 {
1197  QgsDebugMsg( "Entered." );
1198 
1199  theResultFlag = false;
1200 
1201  QDomDocument myDocument;
1202 
1203  // location of problem associated with errorMsg
1204  int line, column;
1205  QString myErrorMessage;
1206 
1207  QFile myFile( theURI );
1208  if ( myFile.open( QFile::ReadOnly ) )
1209  {
1210  // read file
1211  theResultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
1212  if ( !theResultFlag )
1213  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1214  myFile.close();
1215  }
1216  else
1217  {
1218  myErrorMessage = tr( "Unable to open file %1" ).arg( theURI );
1219  }
1220 
1221  if ( !theResultFlag )
1222  {
1223  return myErrorMessage;
1224  }
1225 
1226  // check for root SLD element
1227  QDomElement myRoot = myDocument.firstChildElement( "StyledLayerDescriptor" );
1228  if ( myRoot.isNull() )
1229  {
1230  myErrorMessage = QString( "Error: StyledLayerDescriptor element not found in %1" ).arg( theURI );
1231  theResultFlag = false;
1232  return myErrorMessage;
1233  }
1234 
1235  // now get the style node out and pass it over to the layer
1236  // to deserialise...
1237  QDomElement namedLayerElem = myRoot.firstChildElement( "NamedLayer" );
1238  if ( namedLayerElem.isNull() )
1239  {
1240  myErrorMessage = QString( "Info: NamedLayer element not found." );
1241  theResultFlag = false;
1242  return myErrorMessage;
1243  }
1244 
1245  QString errorMsg;
1246  theResultFlag = readSld( namedLayerElem, errorMsg );
1247  if ( !theResultFlag )
1248  {
1249  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( theURI ).arg( errorMsg );
1250  return myErrorMessage;
1251  }
1252 
1253  return "";
1254 }
1255 
1256 
1258 {
1259  return &mUndoStack;
1260 }
1261 
1262 
1263 void QgsMapLayer::setCustomProperty( const QString& key, const QVariant& value )
1264 {
1265  mCustomProperties[key] = value;
1266 }
1267 
1268 QVariant QgsMapLayer::customProperty( const QString& value, const QVariant& defaultValue ) const
1269 {
1270  return mCustomProperties.value( value, defaultValue );
1271 }
1272 
1273 void QgsMapLayer::removeCustomProperty( const QString& key )
1274 {
1275  mCustomProperties.remove( key );
1276 }
1277 
1278 void QgsMapLayer::readCustomProperties( const QDomNode& layerNode, const QString& keyStartsWith )
1279 {
1280  QDomNode propsNode = layerNode.namedItem( "customproperties" );
1281  if ( propsNode.isNull() ) // no properties stored...
1282  return;
1283 
1284  if ( !keyStartsWith.isEmpty() )
1285  {
1286  //remove old keys
1287  QStringList keysToRemove;
1288  QMap<QString, QVariant>::const_iterator pIt = mCustomProperties.constBegin();
1289  for ( ; pIt != mCustomProperties.constEnd(); ++pIt )
1290  {
1291  if ( pIt.key().startsWith( keyStartsWith ) )
1292  {
1293  keysToRemove.push_back( pIt.key() );
1294  }
1295  }
1296 
1297  QStringList::const_iterator sIt = keysToRemove.constBegin();
1298  for ( ; sIt != keysToRemove.constEnd(); ++sIt )
1299  {
1300  mCustomProperties.remove( *sIt );
1301  }
1302  }
1303  else
1304  {
1305  mCustomProperties.clear();
1306  }
1307 
1308  QDomNodeList nodes = propsNode.childNodes();
1309 
1310  for ( int i = 0; i < nodes.size(); i++ )
1311  {
1312  QDomNode propNode = nodes.at( i );
1313  if ( propNode.isNull() || propNode.nodeName() != "property" )
1314  continue;
1315  QDomElement propElement = propNode.toElement();
1316 
1317  QString key = propElement.attribute( "key" );
1318  if ( key.isEmpty() || key.startsWith( keyStartsWith ) )
1319  {
1320  QString value = propElement.attribute( "value" );
1321  mCustomProperties[key] = QVariant( value );
1322  }
1323  }
1324 
1325 }
1326 
1327 void QgsMapLayer::writeCustomProperties( QDomNode & layerNode, QDomDocument & doc ) const
1328 {
1329  //remove already existing <customproperties> tags
1330  QDomNodeList propertyList = layerNode.toElement().elementsByTagName( "customproperties" );
1331  for ( int i = 0; i < propertyList.size(); ++i )
1332  {
1333  layerNode.removeChild( propertyList.at( i ) );
1334  }
1335 
1336  QDomElement propsElement = doc.createElement( "customproperties" );
1337 
1338  for ( QMap<QString, QVariant>::const_iterator it = mCustomProperties.constBegin(); it != mCustomProperties.constEnd(); ++it )
1339  {
1340  QDomElement propElement = doc.createElement( "property" );
1341  propElement.setAttribute( "key", it.key() );
1342  propElement.setAttribute( "value", it.value().toString() );
1343  propsElement.appendChild( propElement );
1344  }
1345 
1346  layerNode.appendChild( propsElement );
1347 }
1348 
1349 void QgsMapLayer::setCacheImage( QImage * thepImage )
1350 {
1351  QgsDebugMsg( "cache Image set!" );
1352  if ( mpCacheImage == thepImage )
1353  return;
1354 
1355  if ( mpCacheImage )
1356  {
1358  delete mpCacheImage;
1359  }
1360  mpCacheImage = thepImage;
1361 }
1362 
1364 {
1365  return false;
1366 }
1367 
1368 void QgsMapLayer::setValid( bool valid )
1369 {
1370  mValid = valid;
1371 }
1372 
1374 {
1375  setCacheImage( 0 );
1376 }
1377 
1379 {
1380  return QString();
1381 }
1382 
1384 {
1385  mExtent = r;
1386 }