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