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