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