QGIS API Documentation  2.11.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgsmaplayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaplayer.cpp - description
3  -------------------
4  begin : Fri Jun 28 2002
5  copyright : (C) 2002 by Gary E.Sherman
6  email : sherman at mrcc.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 
19 #include <QDateTime>
20 #include <QDir>
21 #include <QDomDocument>
22 #include <QDomElement>
23 #include <QDomImplementation>
24 #include <QDomNode>
25 #include <QFile>
26 #include <QFileInfo>
27 #include <QSettings> // TODO: get rid of it [MD]
28 #include <QTextStream>
29 #include <QUrl>
30 
31 #include <sqlite3.h>
32 
33 #include "qgsapplication.h"
35 #include "qgsdatasourceuri.h"
36 #include "qgslogger.h"
37 #include "qgsmaplayer.h"
38 #include "qgsmaplayerlegend.h"
40 #include "qgspluginlayer.h"
41 #include "qgspluginlayerregistry.h"
43 #include "qgsproject.h"
44 #include "qgsproviderregistry.h"
45 #include "qgsrasterlayer.h"
46 #include "qgsrectangle.h"
47 #include "qgsvectorlayer.h"
48 
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
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 
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.
128  return safeName;
129 }
130 
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  {
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() );
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
296  {
297  bool handled = false;
298 
299  if ( provider == "gdal" )
300  {
301  if ( mDataSource.startsWith( "NETCDF:" ) )
302  {
303  // NETCDF:filename:variable
304  // filename can be quoted with " as it can contain colons
305  QRegExp r( "NETCDF:(.+):([^:]+)" );
306  if ( r.exactMatch( mDataSource ) )
307  {
308  QString filename = r.cap( 1 );
309  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
310  filename = filename.mid( 1, filename.length() - 2 );
311  mDataSource = "NETCDF:\"" + QgsProject::instance()->readPath( filename ) + "\":" + r.cap( 2 );
312  handled = true;
313  }
314  }
315  else if ( mDataSource.startsWith( "HDF4_SDS:" ) )
316  {
317  // HDF4_SDS:subdataset_type:file_name:subdataset_index
318  // filename can be quoted with " as it can contain colons
319  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
320  if ( r.exactMatch( mDataSource ) )
321  {
322  QString filename = r.cap( 2 );
323  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
324  filename = filename.mid( 1, filename.length() - 2 );
325  mDataSource = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + QgsProject::instance()->readPath( filename ) + "\":" + r.cap( 3 );
326  handled = true;
327  }
328  }
329  else if ( mDataSource.startsWith( "HDF5:" ) )
330  {
331  // HDF5:file_name:subdataset
332  // filename can be quoted with " as it can contain colons
333  QRegExp r( "HDF5:(.+):([^:]+)" );
334  if ( r.exactMatch( mDataSource ) )
335  {
336  QString filename = r.cap( 1 );
337  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
338  filename = filename.mid( 1, filename.length() - 2 );
339  mDataSource = "HDF5:\"" + QgsProject::instance()->readPath( filename ) + "\":" + r.cap( 2 );
340  handled = true;
341  }
342  }
343  else if ( mDataSource.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
344  {
345  // NITF_IM:0:filename
346  // RADARSAT_2_CALIB:?:filename
347  QRegExp r( "([^:]+):([^:]+):(.+)" );
348  if ( r.exactMatch( mDataSource ) )
349  {
350  mDataSource = r.cap( 1 ) + ":" + r.cap( 2 ) + ":" + QgsProject::instance()->readPath( r.cap( 3 ) );
351  handled = true;
352  }
353  }
354  }
355 
356  if ( !handled )
358  }
359 
360  // Set the CRS from project file, asking the user if necessary.
361  // Make it the saved CRS to have WMS layer projected correctly.
362  // We will still overwrite whatever GDAL etc picks up anyway
363  // further down this function.
364  mnl = layerElement.namedItem( "layername" );
365  mne = mnl.toElement();
366 
367  QDomNode srsNode = layerElement.namedItem( "srs" );
368  mCRS->readXML( srsNode );
369  mCRS->setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
370  mCRS->validate();
371  savedCRS = *mCRS;
372 
373  // Do not validate any projections in children, they will be overwritten anyway.
374  // No need to ask the user for a projections when it is overwritten, is there?
377 
378  // now let the children grab what they need from the Dom node.
379  layerError = !readXml( layerElement );
380 
381  // overwrite CRS with what we read from project file before the raster/vector
382  // file readnig functions changed it. They will if projections is specfied in the file.
383  // FIXME: is this necessary?
385  *mCRS = savedCRS;
386 
387  // Abort if any error in layer, such as not found.
388  if ( layerError )
389  {
390  return false;
391  }
392 
393  // the internal name is just the data source basename
394  //QFileInfo dataSourceFileInfo( mDataSource );
395  //internalName = dataSourceFileInfo.baseName();
396 
397  // set ID
398  mnl = layerElement.namedItem( "id" );
399  if ( ! mnl.isNull() )
400  {
401  mne = mnl.toElement();
402  if ( ! mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
403  {
404  mID = mne.text();
405  }
406  }
407 
408  // use scale dependent visibility flag
409  setScaleBasedVisibility( layerElement.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
410  setMinimumScale( layerElement.attribute( "minimumScale" ).toFloat() );
411  setMaximumScale( layerElement.attribute( "maximumScale" ).toFloat() );
412 
413  // set name
414  mnl = layerElement.namedItem( "layername" );
415  mne = mnl.toElement();
416  setLayerName( mne.text() );
417 
418  //title
419  QDomElement titleElem = layerElement.firstChildElement( "title" );
420  if ( !titleElem.isNull() )
421  {
422  mTitle = titleElem.text();
423  }
424 
425  //abstract
426  QDomElement abstractElem = layerElement.firstChildElement( "abstract" );
427  if ( !abstractElem.isNull() )
428  {
429  mAbstract = abstractElem.text();
430  }
431 
432  //keywordList
433  QDomElement keywordListElem = layerElement.firstChildElement( "keywordList" );
434  if ( !keywordListElem.isNull() )
435  {
436  QStringList kwdList;
437  for ( QDomNode n = keywordListElem.firstChild(); !n.isNull(); n = n.nextSibling() )
438  {
439  kwdList << n.toElement().text();
440  }
441  mKeywordList = kwdList.join( ", " );
442  }
443 
444  //metadataUrl
445  QDomElement dataUrlElem = layerElement.firstChildElement( "dataUrl" );
446  if ( !dataUrlElem.isNull() )
447  {
448  mDataUrl = dataUrlElem.text();
449  mDataUrlFormat = dataUrlElem.attribute( "format", "" );
450  }
451 
452  //legendUrl
453  QDomElement legendUrlElem = layerElement.firstChildElement( "legendUrl" );
454  if ( !legendUrlElem.isNull() )
455  {
456  mLegendUrl = legendUrlElem.text();
457  mLegendUrlFormat = legendUrlElem.attribute( "format", "" );
458  }
459 
460  //attribution
461  QDomElement attribElem = layerElement.firstChildElement( "attribution" );
462  if ( !attribElem.isNull() )
463  {
464  mAttribution = attribElem.text();
465  mAttributionUrl = attribElem.attribute( "href", "" );
466  }
467 
468  //metadataUrl
469  QDomElement metaUrlElem = layerElement.firstChildElement( "metadataUrl" );
470  if ( !metaUrlElem.isNull() )
471  {
472  mMetadataUrl = metaUrlElem.text();
473  mMetadataUrlType = metaUrlElem.attribute( "type", "" );
474  mMetadataUrlFormat = metaUrlElem.attribute( "format", "" );
475  }
476 
477 #if 0
478  //read transparency level
479  QDomNode transparencyNode = layer_node.namedItem( "transparencyLevelInt" );
480  if ( ! transparencyNode.isNull() )
481  {
482  // set transparency level only if it's in project
483  // (otherwise it sets the layer transparent)
484  QDomElement myElement = transparencyNode.toElement();
485  setTransparency( myElement.text().toInt() );
486  }
487 #endif
488 
489  readCustomProperties( layerElement );
490 
491  return true;
492 } // bool QgsMapLayer::readLayerXML
493 
494 
495 bool QgsMapLayer::readXml( const QDomNode& layer_node )
496 {
497  Q_UNUSED( layer_node );
498  // NOP by default; children will over-ride with behavior specific to them
499 
500  return true;
501 } // void QgsMapLayer::readXml
502 
503 
504 
505 bool QgsMapLayer::writeLayerXML( QDomElement& layerElement, QDomDocument& document, QString relativeBasePath )
506 {
507  // use scale dependent visibility flag
508  layerElement.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
509  layerElement.setAttribute( "minimumScale", QString::number( minimumScale() ) );
510  layerElement.setAttribute( "maximumScale", QString::number( maximumScale() ) );
511 
512  // ID
513  QDomElement layerId = document.createElement( "id" );
514  QDomText layerIdText = document.createTextNode( id() );
515  layerId.appendChild( layerIdText );
516 
517  layerElement.appendChild( layerId );
518 
519  // data source
520  QDomElement dataSource = document.createElement( "datasource" );
521 
522  QString src = source();
523 
524  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
525  // TODO: what about postgres, mysql and others, they should not go through writePath()
526  if ( vlayer && vlayer->providerType() == "spatialite" )
527  {
528  QgsDataSourceURI uri( src );
529  QString database = QgsProject::instance()->writePath( uri.database(), relativeBasePath );
530  uri.setConnection( uri.host(), uri.port(), database, uri.username(), uri.password() );
531  src = uri.uri();
532  }
533  else if ( vlayer && vlayer->providerType() == "ogr" )
534  {
535  QStringList theURIParts = src.split( "|" );
536  theURIParts[0] = QgsProject::instance()->writePath( theURIParts[0], relativeBasePath );
537  src = theURIParts.join( "|" );
538  }
539  else if ( vlayer && vlayer->providerType() == "gpx" )
540  {
541  QStringList theURIParts = src.split( "?" );
542  theURIParts[0] = QgsProject::instance()->writePath( theURIParts[0], relativeBasePath );
543  src = theURIParts.join( "?" );
544  }
545  else if ( vlayer && vlayer->providerType() == "delimitedtext" )
546  {
547  QUrl urlSource = QUrl::fromEncoded( src.toAscii() );
548  QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->writePath( urlSource.toLocalFile(), relativeBasePath ) );
549  urlDest.setQueryItems( urlSource.queryItems() );
550  src = QString::fromAscii( urlDest.toEncoded() );
551  }
552  else
553  {
554  bool handled = false;
555 
556  if ( !vlayer )
557  {
558  QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( this );
559  // Update path for subdataset
560  if ( rlayer && rlayer->providerType() == "gdal" )
561  {
562  if ( src.startsWith( "NETCDF:" ) )
563  {
564  // NETCDF:filename:variable
565  // filename can be quoted with " as it can contain colons
566  QRegExp r( "NETCDF:(.+):([^:]+)" );
567  if ( r.exactMatch( src ) )
568  {
569  QString filename = r.cap( 1 );
570  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
571  filename = filename.mid( 1, filename.length() - 2 );
572  src = "NETCDF:\"" + QgsProject::instance()->writePath( filename, relativeBasePath ) + "\":" + r.cap( 2 );
573  handled = true;
574  }
575  }
576  else if ( src.startsWith( "HDF4_SDS:" ) )
577  {
578  // HDF4_SDS:subdataset_type:file_name:subdataset_index
579  // filename can be quoted with " as it can contain colons
580  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
581  if ( r.exactMatch( src ) )
582  {
583  QString filename = r.cap( 2 );
584  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
585  filename = filename.mid( 1, filename.length() - 2 );
586  src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + QgsProject::instance()->writePath( filename, relativeBasePath ) + "\":" + r.cap( 3 );
587  handled = true;
588  }
589  }
590  else if ( src.startsWith( "HDF5:" ) )
591  {
592  // HDF5:file_name:subdataset
593  // filename can be quoted with " as it can contain colons
594  QRegExp r( "HDF5:(.+):([^:]+)" );
595  if ( r.exactMatch( src ) )
596  {
597  QString filename = r.cap( 1 );
598  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
599  filename = filename.mid( 1, filename.length() - 2 );
600  src = "HDF5:\"" + QgsProject::instance()->writePath( filename, relativeBasePath ) + "\":" + r.cap( 2 );
601  handled = true;
602  }
603  }
604  else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
605  {
606  // NITF_IM:0:filename
607  // RADARSAT_2_CALIB:?:filename
608  QRegExp r( "([^:]+):([^:]+):(.+)" );
609  if ( r.exactMatch( src ) )
610  {
611  src = r.cap( 1 ) + ":" + r.cap( 2 ) + ":" + QgsProject::instance()->writePath( r.cap( 3 ), relativeBasePath );
612  handled = true;
613  }
614  }
615  }
616  }
617 
618  if ( !handled )
619  src = QgsProject::instance()->writePath( src, relativeBasePath );
620  }
621 
622  QDomText dataSourceText = document.createTextNode( src );
623  dataSource.appendChild( dataSourceText );
624 
625  layerElement.appendChild( dataSource );
626 
627 
628  // layer name
629  QDomElement layerName = document.createElement( "layername" );
630  QDomText layerNameText = document.createTextNode( originalName() );
631  layerName.appendChild( layerNameText );
632 
633  // layer title
634  QDomElement layerTitle = document.createElement( "title" );
635  QDomText layerTitleText = document.createTextNode( title() );
636  layerTitle.appendChild( layerTitleText );
637 
638  // layer abstract
639  QDomElement layerAbstract = document.createElement( "abstract" );
640  QDomText layerAbstractText = document.createTextNode( abstract() );
641  layerAbstract.appendChild( layerAbstractText );
642 
643  layerElement.appendChild( layerName );
644  layerElement.appendChild( layerTitle );
645  layerElement.appendChild( layerAbstract );
646 
647  // layer keyword list
648  QStringList keywordStringList = keywordList().split( "," );
649  if ( keywordStringList.size() > 0 )
650  {
651  QDomElement layerKeywordList = document.createElement( "keywordList" );
652  for ( int i = 0; i < keywordStringList.size(); ++i )
653  {
654  QDomElement layerKeywordValue = document.createElement( "value" );
655  QDomText layerKeywordText = document.createTextNode( keywordStringList.at( i ).trimmed() );
656  layerKeywordValue.appendChild( layerKeywordText );
657  layerKeywordList.appendChild( layerKeywordValue );
658  }
659  layerElement.appendChild( layerKeywordList );
660  }
661 
662  // layer metadataUrl
663  QString aDataUrl = dataUrl();
664  if ( !aDataUrl.isEmpty() )
665  {
666  QDomElement layerDataUrl = document.createElement( "dataUrl" );
667  QDomText layerDataUrlText = document.createTextNode( aDataUrl );
668  layerDataUrl.appendChild( layerDataUrlText );
669  layerDataUrl.setAttribute( "format", dataUrlFormat() );
670  layerElement.appendChild( layerDataUrl );
671  }
672 
673  // layer legendUrl
674  QString aLegendUrl = legendUrl();
675  if ( !aLegendUrl.isEmpty() )
676  {
677  QDomElement layerLegendUrl = document.createElement( "legendUrl" );
678  QDomText layerLegendUrlText = document.createTextNode( aLegendUrl );
679  layerLegendUrl.appendChild( layerLegendUrlText );
680  layerLegendUrl.setAttribute( "format", legendUrlFormat() );
681  layerElement.appendChild( layerLegendUrl );
682  }
683 
684  // layer attribution
685  QString aAttribution = attribution();
686  if ( !aAttribution.isEmpty() )
687  {
688  QDomElement layerAttribution = document.createElement( "attribution" );
689  QDomText layerAttributionText = document.createTextNode( aAttribution );
690  layerAttribution.appendChild( layerAttributionText );
691  layerAttribution.setAttribute( "href", attributionUrl() );
692  layerElement.appendChild( layerAttribution );
693  }
694 
695  // layer metadataUrl
696  QString aMetadataUrl = metadataUrl();
697  if ( !aMetadataUrl.isEmpty() )
698  {
699  QDomElement layerMetadataUrl = document.createElement( "metadataUrl" );
700  QDomText layerMetadataUrlText = document.createTextNode( aMetadataUrl );
701  layerMetadataUrl.appendChild( layerMetadataUrlText );
702  layerMetadataUrl.setAttribute( "type", metadataUrlType() );
703  layerMetadataUrl.setAttribute( "format", metadataUrlFormat() );
704  layerElement.appendChild( layerMetadataUrl );
705  }
706 
707  // timestamp if supported
708  if ( timestamp() > QDateTime() )
709  {
710  QDomElement stamp = document.createElement( "timestamp" );
711  QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
712  stamp.appendChild( stampText );
713  layerElement.appendChild( stamp );
714  }
715 
716  layerElement.appendChild( layerName );
717 
718  // zorder
719  // This is no longer stored in the project file. It is superfluous since the layers
720  // are written and read in the proper order.
721 
722  // spatial reference system id
723  QDomElement mySrsElement = document.createElement( "srs" );
724  mCRS->writeXML( mySrsElement, document );
725  layerElement.appendChild( mySrsElement );
726 
727 #if 0
728  // <transparencyLevelInt>
729  QDomElement transparencyLevelIntElement = document.createElement( "transparencyLevelInt" );
730  QDomText transparencyLevelIntText = document.createTextNode( QString::number( getTransparency() ) );
731  transparencyLevelIntElement.appendChild( transparencyLevelIntText );
732  maplayer.appendChild( transparencyLevelIntElement );
733 #endif
734 
735  // now append layer node to map layer node
736 
737  writeCustomProperties( layerElement, document );
738 
739  return writeXml( layerElement, document );
740 
741 } // bool QgsMapLayer::writeXML
742 
744 {
745  QDomDocument doc( "qgis-layer-definition" );
746  QDomElement qgiselm = doc.createElement( "qlr" );
747  doc.appendChild( qgiselm );
748  QDomElement layerselm = doc.createElement( "maplayers" );
749  foreach ( QgsMapLayer* layer, layers )
750  {
751  QDomElement layerelm = doc.createElement( "maplayer" );
752  layer->writeLayerXML( layerelm, doc, relativeBasePath );
753  layerselm.appendChild( layerelm );
754  }
755  qgiselm.appendChild( layerselm );
756  return doc;
757 }
758 
760 {
761  QList<QgsMapLayer*> layers;
762  QDomNodeList layernodes = document.elementsByTagName( "maplayer" );
763  for ( int i = 0; i < layernodes.size(); ++i )
764  {
765  QDomNode layernode = layernodes.at( i );
766  QDomElement layerElem = layernode.toElement();
767 
768  QString type = layerElem.attribute( "type" );
769  QgsDebugMsg( type );
770  QgsMapLayer *layer = 0;
771 
772  if ( type == "vector" )
773  {
774  layer = new QgsVectorLayer;
775  }
776  else if ( type == "raster" )
777  {
778  layer = new QgsRasterLayer;
779  }
780  else if ( type == "plugin" )
781  {
782  QString typeName = layerElem.attribute( "name" );
783  layer = QgsPluginLayerRegistry::instance()->createLayer( typeName );
784  }
785 
786  if ( !layer )
787  continue;
788 
789  bool ok = layer->readLayerXML( layerElem );
790  if ( ok )
791  layers << layer;
792  }
793  return layers;
794 }
795 
797 {
798  QFile file( qlrfile );
799  if ( !file.open( QIODevice::ReadOnly ) )
800  {
801  QgsDebugMsg( "Can't open file" );
802  return QList<QgsMapLayer*>();
803  }
804 
805  QDomDocument doc;
806  if ( !doc.setContent( &file ) )
807  {
808  QgsDebugMsg( "Can't set content" );
809  return QList<QgsMapLayer*>();
810  }
811 
812  QFileInfo fileinfo( file );
813  QDir::setCurrent( fileinfo.absoluteDir().path() );
814  return QgsMapLayer::fromLayerDefinition( doc );
815 }
816 
817 
818 bool QgsMapLayer::writeXml( QDomNode & layer_node, QDomDocument & document )
819 {
820  Q_UNUSED( layer_node );
821  Q_UNUSED( document );
822  // NOP by default; children will over-ride with behavior specific to them
823 
824  return true;
825 } // void QgsMapLayer::writeXml
826 
827 
828 void QgsMapLayer::readCustomProperties( const QDomNode &layerNode, const QString &keyStartsWith )
829 {
830  mCustomProperties.readXml( layerNode, keyStartsWith );
831 }
832 
834 {
835  mCustomProperties.writeXml( layerNode, doc );
836 }
837 
838 void QgsMapLayer::readStyleManager( const QDomNode& layerNode )
839 {
840  QDomElement styleMgrElem = layerNode.firstChildElement( "map-layer-style-manager" );
841  if ( !styleMgrElem.isNull() )
842  mStyleManager->readXml( styleMgrElem );
843  else
844  mStyleManager->reset();
845 }
846 
848 {
849  if ( mStyleManager )
850  {
851  QDomElement styleMgrElem = doc.createElement( "map-layer-style-manager" );
852  mStyleManager->writeXml( styleMgrElem );
853  layerNode.appendChild( styleMgrElem );
854  }
855 }
856 
857 
858 
859 
861 {
862  return mValid;
863 }
864 
865 
867 {
868  QgsDebugMsg( "called" );
869  // TODO: emit a signal - it will be used to update legend
870 }
871 
872 
874 {
875  return QString();
876 }
877 
879 {
880  return QString();
881 }
882 
883 #if 0
884 void QgsMapLayer::connectNotify( const char * signal )
885 {
886  Q_UNUSED( signal );
887  QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
888 } // QgsMapLayer::connectNotify
889 #endif
890 
891 
892 void QgsMapLayer::toggleScaleBasedVisibility( bool theVisibilityFlag )
893 {
894  setScaleBasedVisibility( theVisibilityFlag );
895 }
896 
898 {
899  return mScaleBasedVisibility;
900 }
901 
902 void QgsMapLayer::setMinimumScale( const float theMinScale )
903 {
904  mMinScale = theMinScale;
905 }
906 
908 {
909  return mMinScale;
910 }
911 
912 
913 void QgsMapLayer::setMaximumScale( const float theMaxScale )
914 {
915  mMaxScale = theMaxScale;
916 }
917 
918 void QgsMapLayer::setScaleBasedVisibility( const bool enabled )
919 {
920  mScaleBasedVisibility = enabled;
921 }
922 
924 {
925  return mMaxScale;
926 }
927 
929 {
930  return QStringList(); // Empty
931 }
932 
934 {
935  Q_UNUSED( layers );
936  // NOOP
937 }
938 
940 {
941  Q_UNUSED( name );
942  Q_UNUSED( vis );
943  // NOOP
944 }
945 
947 {
948  return *mCRS;
949 }
950 
951 void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem& srs, bool emitSignal )
952 {
953  *mCRS = srs;
954 
955  if ( !mCRS->isValid() )
956  {
957  mCRS->setValidationHint( tr( "Specify CRS for layer %1" ).arg( name() ) );
958  mCRS->validate();
959  }
960 
961  if ( emitSignal )
962  emit layerCrsChanged();
963 }
964 
966 {
967  // Capitalise the first letter of the layer name if requested
968  QSettings settings;
969  bool capitaliseLayerName =
970  settings.value( "/qgis/capitaliseLayerName", QVariant( false ) ).toBool();
971 
972  QString layerName( name );
973 
974  if ( capitaliseLayerName )
975  layerName = layerName.left( 1 ).toUpper() + layerName.mid( 1 );
976 
977  return layerName;
978 }
979 
981 {
982  QString myURI = publicSource();
983 
984  // if file is using the VSIFILE mechanism, remove the prefix
985  if ( myURI.startsWith( "/vsigzip/", Qt::CaseInsensitive ) )
986  {
987  myURI.remove( 0, 9 );
988  }
989  else if ( myURI.startsWith( "/vsizip/", Qt::CaseInsensitive ) &&
990  myURI.endsWith( ".zip", Qt::CaseInsensitive ) )
991  {
992  // ideally we should look for .qml file inside zip file
993  myURI.remove( 0, 8 );
994  }
995  else if ( myURI.startsWith( "/vsitar/", Qt::CaseInsensitive ) &&
996  ( myURI.endsWith( ".tar", Qt::CaseInsensitive ) ||
997  myURI.endsWith( ".tar.gz", Qt::CaseInsensitive ) ||
998  myURI.endsWith( ".tgz", Qt::CaseInsensitive ) ) )
999  {
1000  // ideally we should look for .qml file inside tar file
1001  myURI.remove( 0, 8 );
1002  }
1003 
1004  QFileInfo myFileInfo( myURI );
1005  QString key;
1006 
1007  if ( myFileInfo.exists() )
1008  {
1009  // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name
1010  if ( myURI.endsWith( ".gz", Qt::CaseInsensitive ) )
1011  myURI.chop( 3 );
1012  else if ( myURI.endsWith( ".zip", Qt::CaseInsensitive ) )
1013  myURI.chop( 4 );
1014  else if ( myURI.endsWith( ".tar", Qt::CaseInsensitive ) )
1015  myURI.chop( 4 );
1016  else if ( myURI.endsWith( ".tar.gz", Qt::CaseInsensitive ) )
1017  myURI.chop( 7 );
1018  else if ( myURI.endsWith( ".tgz", Qt::CaseInsensitive ) )
1019  myURI.chop( 4 );
1020  myFileInfo.setFile( myURI );
1021  // get the file name for our .qml style file
1022  key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
1023  }
1024  else
1025  {
1026  key = publicSource();
1027  }
1028 
1029  return key;
1030 }
1031 
1033 {
1034  return loadNamedStyle( styleURI(), theResultFlag );
1035 }
1036 
1037 bool QgsMapLayer::loadNamedStyleFromDb( const QString &db, const QString &theURI, QString &qml )
1038 {
1039  QgsDebugMsg( QString( "db = %1 uri = %2" ).arg( db ).arg( theURI ) );
1040 
1041  bool theResultFlag = false;
1042 
1043  // read from database
1044  sqlite3 *myDatabase;
1045  sqlite3_stmt *myPreparedStatement;
1046  const char *myTail;
1047  int myResult;
1048 
1049  QgsDebugMsg( QString( "Trying to load style for \"%1\" from \"%2\"" ).arg( theURI ).arg( db ) );
1050 
1051  if ( db.isEmpty() || !QFile( db ).exists() )
1052  return false;
1053 
1054  myResult = sqlite3_open_v2( db.toUtf8().data(), &myDatabase, SQLITE_OPEN_READONLY, NULL );
1055  if ( myResult != SQLITE_OK )
1056  {
1057  return false;
1058  }
1059 
1060  QString mySql = "select qml from tbl_styles where style=?";
1061  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1062  if ( myResult == SQLITE_OK )
1063  {
1064  QByteArray param = theURI.toUtf8();
1065 
1066  if ( sqlite3_bind_text( myPreparedStatement, 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
1067  sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
1068  {
1069  qml = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
1070  theResultFlag = true;
1071  }
1072 
1073  sqlite3_finalize( myPreparedStatement );
1074  }
1075 
1076  sqlite3_close( myDatabase );
1077 
1078  return theResultFlag;
1079 }
1080 
1081 
1082 QString QgsMapLayer::loadNamedStyle( const QString &theURI, bool &theResultFlag )
1083 {
1084  QgsDebugMsg( QString( "uri = %1 myURI = %2" ).arg( theURI ).arg( publicSource() ) );
1085 
1086  theResultFlag = false;
1087 
1088  QDomDocument myDocument( "qgis" );
1089 
1090  // location of problem associated with errorMsg
1091  int line, column;
1092  QString myErrorMessage;
1093 
1094  QFile myFile( theURI );
1095  if ( myFile.open( QFile::ReadOnly ) )
1096  {
1097  // read file
1098  theResultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
1099  if ( !theResultFlag )
1100  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1101  myFile.close();
1102  }
1103  else
1104  {
1105  QFileInfo project( QgsProject::instance()->fileName() );
1106  QgsDebugMsg( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ) );
1107 
1108  QString qml;
1109  if ( loadNamedStyleFromDb( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ), theURI, qml ) ||
1110  ( project.exists() && loadNamedStyleFromDb( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), theURI, qml ) ) ||
1111  loadNamedStyleFromDb( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( "resources/qgis.qmldb" ), theURI, qml ) )
1112  {
1113  theResultFlag = myDocument.setContent( qml, &myErrorMessage, &line, &column );
1114  if ( !theResultFlag )
1115  {
1116  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1117  }
1118  }
1119  else
1120  {
1121  myErrorMessage = tr( "Style not found in database" );
1122  }
1123  }
1124 
1125  if ( !theResultFlag )
1126  {
1127  return myErrorMessage;
1128  }
1129 
1130  theResultFlag = importNamedStyle( myDocument, myErrorMessage );
1131  if ( !theResultFlag )
1132  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( theURI ).arg( myErrorMessage );
1133 
1134  return myErrorMessage;
1135 }
1136 
1137 
1138 bool QgsMapLayer::importNamedStyle( QDomDocument& myDocument, QString& myErrorMessage )
1139 {
1140  // get style file version string, if any
1141  QgsProjectVersion fileVersion( myDocument.firstChildElement( "qgis" ).attribute( "version" ) );
1142  QgsProjectVersion thisVersion( QGis::QGIS_VERSION );
1143 
1144  if ( thisVersion > fileVersion )
1145  {
1146  QgsLogger::warning( "Loading a style file that was saved with an older "
1147  "version of qgis (saved in " + fileVersion.text() +
1148  ", loaded in " + QGis::QGIS_VERSION +
1149  "). Problems may occur." );
1150 
1151  QgsProjectFileTransform styleFile( myDocument, fileVersion );
1152  // styleFile.dump();
1153  styleFile.updateRevision( thisVersion );
1154  // styleFile.dump();
1155  }
1156 
1157  // now get the layer node out and pass it over to the layer
1158  // to deserialise...
1159  QDomElement myRoot = myDocument.firstChildElement( "qgis" );
1160  if ( myRoot.isNull() )
1161  {
1162  myErrorMessage = tr( "Root <qgis> element could not be found" );
1163  return false;
1164  }
1165 
1166  // use scale dependent visibility flag
1167  setScaleBasedVisibility( myRoot.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
1168  setMinimumScale( myRoot.attribute( "minimumScale" ).toFloat() );
1169  setMaximumScale( myRoot.attribute( "maximumScale" ).toFloat() );
1170 
1171 #if 0
1172  //read transparency level
1173  QDomNode transparencyNode = myRoot.namedItem( "transparencyLevelInt" );
1174  if ( ! transparencyNode.isNull() )
1175  {
1176  // set transparency level only if it's in project
1177  // (otherwise it sets the layer transparent)
1178  QDomElement myElement = transparencyNode.toElement();
1179  setTransparency( myElement.text().toInt() );
1180  }
1181 #endif
1182 
1183  return readSymbology( myRoot, myErrorMessage );
1184 }
1185 
1187 {
1188  QDomImplementation DomImplementation;
1189  QDomDocumentType documentType = DomImplementation.createDocumentType( "qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" );
1190  QDomDocument myDocument( documentType );
1191 
1192  QDomElement myRootNode = myDocument.createElement( "qgis" );
1193  myRootNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
1194  myDocument.appendChild( myRootNode );
1195 
1196  myRootNode.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
1197  myRootNode.setAttribute( "minimumScale", QString::number( minimumScale() ) );
1198  myRootNode.setAttribute( "maximumScale", QString::number( maximumScale() ) );
1199 
1200 #if 0
1201  // <transparencyLevelInt>
1202  QDomElement transparencyLevelIntElement = myDocument.createElement( "transparencyLevelInt" );
1203  QDomText transparencyLevelIntText = myDocument.createTextNode( QString::number( getTransparency() ) );
1204  transparencyLevelIntElement.appendChild( transparencyLevelIntText );
1205  myRootNode.appendChild( transparencyLevelIntElement );
1206 #endif
1207 
1208  if ( !writeSymbology( myRootNode, myDocument, errorMsg ) )
1209  {
1210  errorMsg = QObject::tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1211  return;
1212  }
1213  doc = myDocument;
1214 }
1215 
1217 {
1218  return saveNamedStyle( styleURI(), theResultFlag );
1219 }
1220 
1221 QString QgsMapLayer::saveNamedStyle( const QString &theURI, bool &theResultFlag )
1222 {
1223  QString myErrorMessage;
1224  QDomDocument myDocument;
1225  exportNamedStyle( myDocument, myErrorMessage );
1226 
1227  // check if the uri is a file or ends with .qml,
1228  // which indicates that it should become one
1229  // everything else goes to the database
1230  QString filename;
1231 
1232  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1233  if ( vlayer && vlayer->providerType() == "ogr" )
1234  {
1235  QStringList theURIParts = theURI.split( "|" );
1236  filename = theURIParts[0];
1237  }
1238  else if ( vlayer && vlayer->providerType() == "gpx" )
1239  {
1240  QStringList theURIParts = theURI.split( "?" );
1241  filename = theURIParts[0];
1242  }
1243  else if ( vlayer && vlayer->providerType() == "delimitedtext" )
1244  {
1245  filename = QUrl::fromEncoded( theURI.toAscii() ).toLocalFile();
1246  }
1247  else
1248  {
1249  filename = theURI;
1250  }
1251 
1252  QFileInfo myFileInfo( filename );
1253  if ( myFileInfo.exists() || filename.endsWith( ".qml", Qt::CaseInsensitive ) )
1254  {
1255  QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1256  if ( !myDirInfo.isWritable() )
1257  {
1258  return tr( "The directory containing your dataset needs to be writable!" );
1259  }
1260 
1261  // now construct the file name for our .qml style file
1262  QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
1263 
1264  QFile myFile( myFileName );
1265  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1266  {
1267  QTextStream myFileStream( &myFile );
1268  // save as utf-8 with 2 spaces for indents
1269  myDocument.save( myFileStream, 2 );
1270  myFile.close();
1271  theResultFlag = true;
1272  return tr( "Created default style file as %1" ).arg( myFileName );
1273  }
1274  else
1275  {
1276  theResultFlag = false;
1277  return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
1278  }
1279  }
1280  else
1281  {
1282  QString qml = myDocument.toString();
1283 
1284  // read from database
1285  sqlite3 *myDatabase;
1286  sqlite3_stmt *myPreparedStatement;
1287  const char *myTail;
1288  int myResult;
1289 
1290  myResult = sqlite3_open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ).toUtf8().data(), &myDatabase );
1291  if ( myResult != SQLITE_OK )
1292  {
1293  return tr( "User database could not be opened." );
1294  }
1295 
1296  QByteArray param0 = theURI.toUtf8();
1297  QByteArray param1 = qml.toUtf8();
1298 
1299  QString mySql = "create table if not exists tbl_styles(style varchar primary key,qml varchar)";
1300  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1301  if ( myResult == SQLITE_OK )
1302  {
1303  if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
1304  {
1305  sqlite3_finalize( myPreparedStatement );
1306  sqlite3_close( myDatabase );
1307  theResultFlag = false;
1308  return tr( "The style table could not be created." );
1309  }
1310  }
1311 
1312  sqlite3_finalize( myPreparedStatement );
1313 
1314  mySql = "insert into tbl_styles(style,qml) values (?,?)";
1315  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1316  if ( myResult == SQLITE_OK )
1317  {
1318  if ( sqlite3_bind_text( myPreparedStatement, 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
1319  sqlite3_bind_text( myPreparedStatement, 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
1320  sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
1321  {
1322  theResultFlag = true;
1323  myErrorMessage = tr( "The style %1 was saved to database" ).arg( theURI );
1324  }
1325  }
1326 
1327  sqlite3_finalize( myPreparedStatement );
1328 
1329  if ( !theResultFlag )
1330  {
1331  QString mySql = "update tbl_styles set qml=? where style=?";
1332  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
1333  if ( myResult == SQLITE_OK )
1334  {
1335  if ( sqlite3_bind_text( myPreparedStatement, 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
1336  sqlite3_bind_text( myPreparedStatement, 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
1337  sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
1338  {
1339  theResultFlag = true;
1340  myErrorMessage = tr( "The style %1 was updated in the database." ).arg( theURI );
1341  }
1342  else
1343  {
1344  theResultFlag = false;
1345  myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( theURI );
1346  }
1347  }
1348  else
1349  {
1350  theResultFlag = false;
1351  myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( theURI );
1352  }
1353 
1354  sqlite3_finalize( myPreparedStatement );
1355  }
1356 
1357  sqlite3_close( myDatabase );
1358  }
1359 
1360  return myErrorMessage;
1361 }
1362 
1364 {
1365  QDomDocument myDocument = QDomDocument();
1366 
1367  QDomNode header = myDocument.createProcessingInstruction( "xml", "version=\"1.0\" encoding=\"UTF-8\"" );
1368  myDocument.appendChild( header );
1369 
1370  // Create the root element
1371  QDomElement root = myDocument.createElementNS( "http://www.opengis.net/sld", "StyledLayerDescriptor" );
1372  root.setAttribute( "version", "1.1.0" );
1373  root.setAttribute( "xsi:schemaLocation", "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" );
1374  root.setAttribute( "xmlns:ogc", "http://www.opengis.net/ogc" );
1375  root.setAttribute( "xmlns:se", "http://www.opengis.net/se" );
1376  root.setAttribute( "xmlns:xlink", "http://www.w3.org/1999/xlink" );
1377  root.setAttribute( "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance" );
1378  myDocument.appendChild( root );
1379 
1380  // Create the NamedLayer element
1381  QDomElement namedLayerNode = myDocument.createElement( "NamedLayer" );
1382  root.appendChild( namedLayerNode );
1383 
1384  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1385  if ( !vlayer )
1386  {
1387  errorMsg = tr( "Could not save symbology because:\n%1" )
1388  .arg( "Non-vector layers not supported yet" );
1389  return;
1390  }
1391 
1392  if ( !vlayer->writeSld( namedLayerNode, myDocument, errorMsg ) )
1393  {
1394  errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1395  return;
1396  }
1397 
1398  doc = myDocument;
1399 }
1400 
1401 QString QgsMapLayer::saveSldStyle( const QString &theURI, bool &theResultFlag )
1402 {
1403  QString errorMsg;
1404  QDomDocument myDocument;
1405  exportSldStyle( myDocument, errorMsg );
1406  if ( !errorMsg.isNull() )
1407  {
1408  theResultFlag = false;
1409  return errorMsg;
1410  }
1411  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1412 
1413  // check if the uri is a file or ends with .sld,
1414  // which indicates that it should become one
1415  QString filename;
1416  if ( vlayer->providerType() == "ogr" )
1417  {
1418  QStringList theURIParts = theURI.split( "|" );
1419  filename = theURIParts[0];
1420  }
1421  else if ( vlayer->providerType() == "gpx" )
1422  {
1423  QStringList theURIParts = theURI.split( "?" );
1424  filename = theURIParts[0];
1425  }
1426  else if ( vlayer->providerType() == "delimitedtext" )
1427  {
1428  filename = QUrl::fromEncoded( theURI.toAscii() ).toLocalFile();
1429  }
1430  else
1431  {
1432  filename = theURI;
1433  }
1434 
1435  QFileInfo myFileInfo( filename );
1436  if ( myFileInfo.exists() || filename.endsWith( ".sld", Qt::CaseInsensitive ) )
1437  {
1438  QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1439  if ( !myDirInfo.isWritable() )
1440  {
1441  return tr( "The directory containing your dataset needs to be writable!" );
1442  }
1443 
1444  // now construct the file name for our .sld style file
1445  QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".sld";
1446 
1447  QFile myFile( myFileName );
1448  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1449  {
1450  QTextStream myFileStream( &myFile );
1451  // save as utf-8 with 2 spaces for indents
1452  myDocument.save( myFileStream, 2 );
1453  myFile.close();
1454  theResultFlag = true;
1455  return tr( "Created default style file as %1" ).arg( myFileName );
1456  }
1457  }
1458 
1459  theResultFlag = false;
1460  return tr( "ERROR: Failed to created SLD style file as %1. Check file permissions and retry." ).arg( filename );
1461 }
1462 
1463 QString QgsMapLayer::loadSldStyle( const QString &theURI, bool &theResultFlag )
1464 {
1465  QgsDebugMsg( "Entered." );
1466 
1467  theResultFlag = false;
1468 
1469  QDomDocument myDocument;
1470 
1471  // location of problem associated with errorMsg
1472  int line, column;
1473  QString myErrorMessage;
1474 
1475  QFile myFile( theURI );
1476  if ( myFile.open( QFile::ReadOnly ) )
1477  {
1478  // read file
1479  theResultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
1480  if ( !theResultFlag )
1481  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1482  myFile.close();
1483  }
1484  else
1485  {
1486  myErrorMessage = tr( "Unable to open file %1" ).arg( theURI );
1487  }
1488 
1489  if ( !theResultFlag )
1490  {
1491  return myErrorMessage;
1492  }
1493 
1494  // check for root SLD element
1495  QDomElement myRoot = myDocument.firstChildElement( "StyledLayerDescriptor" );
1496  if ( myRoot.isNull() )
1497  {
1498  myErrorMessage = QString( "Error: StyledLayerDescriptor element not found in %1" ).arg( theURI );
1499  theResultFlag = false;
1500  return myErrorMessage;
1501  }
1502 
1503  // now get the style node out and pass it over to the layer
1504  // to deserialise...
1505  QDomElement namedLayerElem = myRoot.firstChildElement( "NamedLayer" );
1506  if ( namedLayerElem.isNull() )
1507  {
1508  myErrorMessage = QString( "Info: NamedLayer element not found." );
1509  theResultFlag = false;
1510  return myErrorMessage;
1511  }
1512 
1513  QString errorMsg;
1514  theResultFlag = readSld( namedLayerElem, errorMsg );
1515  if ( !theResultFlag )
1516  {
1517  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( theURI ).arg( errorMsg );
1518  return myErrorMessage;
1519  }
1520 
1521  return "";
1522 }
1523 
1524 
1526 {
1527  return &mUndoStack;
1528 }
1529 
1530 
1531 void QgsMapLayer::setCustomProperty( const QString& key, const QVariant& value )
1532 {
1533  mCustomProperties.setValue( key, value );
1534 }
1535 
1536 QVariant QgsMapLayer::customProperty( const QString& value, const QVariant& defaultValue ) const
1537 {
1538  return mCustomProperties.value( value, defaultValue );
1539 }
1540 
1542 {
1543  mCustomProperties.remove( key );
1544 }
1545 
1546 
1547 
1549 {
1550  return false;
1551 }
1552 
1553 void QgsMapLayer::setValid( bool valid )
1554 {
1555  mValid = valid;
1556 }
1557 
1559 {
1560  emit repaintRequested();
1561 }
1562 
1564 {
1565  if ( legend == mLegend )
1566  return;
1567 
1568  delete mLegend;
1569  mLegend = legend;
1570 
1571  if ( mLegend )
1572  connect( mLegend, SIGNAL( itemsChanged() ), this, SIGNAL( legendChanged() ) );
1573 
1574  emit legendChanged();
1575 }
1576 
1578 {
1579  return mLegend;
1580 }
1581 
1583 {
1584  return mStyleManager;
1585 }
1586 
1588 {
1589  emit repaintRequested();
1590 }
1591 
1593 {
1594  emit repaintRequested();
1595 }
1596 
1598 {
1599  return QString();
1600 }
1601 
1603 {
1604  mExtent = r;
1605 }
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.
QString fromAscii(const char *str, int size)
QString toString(Qt::DateFormat format) const
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
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Base class for all map layer types.
Definition: qgsmaplayer.h:49
QString cap(int nth) const
QString writePath(QString filename, QString relativeBasePath=QString::null) const
prepare a filename to save it to the project file
QString toUpper() const
QDomProcessingInstruction createProcessingInstruction(const QString &target, const QString &data)
QgsMapLayer::LayerType type() const
Get the type of the layer.
Definition: qgsmaplayer.cpp:93
QString path() const
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:596
QString mKeywordList
Definition: qgsmaplayer.h:588
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
QDomNode appendChild(const QDomNode &newChild)
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:592
QString attribute(const QString &name, const QString &defValue) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
QString toString(int indent) const
static QString removePassword(const QString &aUri)
Removes password element from uris.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
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
QList< QPair< QString, QString > > queryItems() const
void setFile(const QString &file)
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:605
const T & at(int i) const
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
QString join(const QString &separator) const
int length() const
bool exists() const
QString & remove(int position, int n)
static CUSTOM_CRS_VALIDATION customSrsValidation()
Gets custom function.
QDomElement createElementNS(const QString &nsURI, const QString &qName)
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 ...
void chop(int n)
static const QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
QChar separator()
QString tr(const char *sourceText, const char *disambiguation, int n)
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:578
virtual bool writeSymbology(QDomNode &node, QDomDocument &doc, QString &errorMessage) const =0
Write the symbology for the layer into the docment provided.
int size() const
bool isNull() const
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:378
static QString capitaliseLayerName(const QString &name)
A convenience function to (un)capitalise the layer name.
QDomElement toElement() const
void setPath(const QString &path)
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:569
QString mMetadataUrl
MetadataUrl of the layer.
Definition: qgsmaplayer.h:599
QString number(int n, int base)
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
QString fromUtf8(const char *str, int size)
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).
QString text() const
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 path() const
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 setAttribute(const QString &name, const QString &value)
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.
int toInt(bool *ok, int base) const
virtual void setExtent(const QgsRectangle &rect)
Set the extent.
void readXml(const QDomElement &mgrElement)
Read configuration (for project loading)
QDomNodeList elementsByTagName(const QString &tagname) const
QString absoluteFilePath() const
bool isEmpty() const
QString mDataUrl
DataUrl of the layer.
Definition: qgsmaplayer.h:591
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
void setScheme(const QString &scheme)
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 path() const
QDir absoluteDir() const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
bool setCurrent(const QString &path)
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:572
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
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:584
virtual bool readSld(const QDomNode &node, QString &errorMessage)
Definition: qgsmaplayer.h:353
QString toLocalFile() const
QString mMetadataUrlFormat
Definition: qgsmaplayer.h:601
QDomText createTextNode(const QString &value)
virtual void connectNotify(const char *signal)
bool exists() const
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
QDomNode namedItem(const QString &name) const
struct sqlite3 sqlite3
bool contains(QChar ch, Qt::CaseSensitivity cs) const
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:595
QString mAbstract
Description of the layer.
Definition: qgsmaplayer.h:587
virtual void close()
QgsMapLayerLegend * legend() const
Can be null.
bool isNull() const
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.
QString & replace(int position, int n, QChar after)
QVariant value(const QString &key, const QVariant &defaultValue) const
bool writeXML(QDomNode &theNode, QDomDocument &theDoc) const
Contains information about the context of a rendering operation.
QDateTime currentDateTime()
void save(QTextStream &str, int indent) const
QDomNode firstChild() const
QString mid(int position, int n) const
Q_DECL_DEPRECATED void clearCacheImage()
Clear cached image.
QString mDataSource
data source description string, varies by layer type
Definition: qgsmaplayer.h:575
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.
T takeFirst()
float toFloat(bool *ok) const
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
QDomElement firstChildElement(const QString &tagName) const
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:604
Class for storing a coordinate reference system (CRS)
QString absoluteFilePath(const QString &fileName) const
void legendChanged()
Signal emitted when legend of the layer has changed.
int length() const
bool toBool() const
char * data()
QString left(int n) const
void setQueryItems(const QList< QPair< QString, QString > > &query)
QString completeBaseName() const
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:485
int size() const
QDomElement createElement(const QString &tagName)
const QString & attributionUrl() const
Definition: qgsmaplayer.h:112
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
void setLegend(QgsMapLayerLegend *legend)
Assign a legend controller to the map layer.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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
QDomDocumentType createDocumentType(const QString &qName, const QString &publicId, const QString &systemId)
const QString & legendUrlFormat() const
Definition: qgsmaplayer.h:380
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...
bool exactMatch(const QString &str) const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
const QString & title() const
Definition: qgsmaplayer.h:94
QString mLayerOrigName
Original name of the layer.
Definition: qgsmaplayer.h:582
void setValid(bool valid)
set whether layer is valid or not - should be used in constructor.
QString baseName() const
QString username() const
QString mMetadataUrlType
Definition: qgsmaplayer.h:600
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.
QUrl fromEncoded(const QByteArray &input)
QByteArray toEncoded(QFlags< QUrl::FormattingOption > options) const
QString port() const
const QString & dataUrlFormat() const
Definition: qgsmaplayer.h:106
QByteArray toAscii() const
QUrl fromLocalFile(const QString &localFile)
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
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
QDomNode at(int index) const
void writeXml(QDomElement &mgrElement) const
Write configuration (for project saving)
QByteArray toUtf8() const