Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgsmaplayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaplayer.cpp - description
3  -------------------
4  begin : Fri Jun 28 2002
5  copyright : (C) 2002 by Gary E.Sherman
6  email : sherman at mrcc.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 /* $Id$ */
18 
19 
20 #include <QDateTime>
21 #include <QDomNode>
22 #include <QFileInfo>
23 #include <QSettings> // TODO: get rid of it [MD]
24 #include <QDir>
25 #include <QFile>
26 #include <QDomDocument>
27 #include <QDomElement>
28 #include <QDomImplementation>
29 #include <QTextStream>
30 #include <QUrl>
31 
32 #include <sqlite3.h>
33 
34 #include "qgslogger.h"
35 #include "qgsrectangle.h"
36 #include "qgssymbol.h"
37 #include "qgsmaplayer.h"
39 #include "qgsapplication.h"
40 #include "qgsproject.h"
41 #include "qgsdatasourceuri.h"
42 #include "qgsvectorlayer.h"
43 
45  QString lyrname,
46  QString source ) :
47  mTransparencyLevel( 255 ), // 0 is completely transparent
48  mValid( false ), // assume the layer is invalid
49  mDataSource( source ),
50  mID( "" ),
51  mLayerType( type )
52 
53 {
54  QgsDebugMsg( "lyrname is '" + lyrname + "'" );
55 
57 
58  // Set the display name = internal name
59  mLayerName = capitaliseLayerName( lyrname );
60  QgsDebugMsg( "layerName is '" + mLayerName + "'" );
61 
62  // Generate the unique ID of this layer
63  QDateTime dt = QDateTime::currentDateTime();
64  mID = lyrname + dt.toString( "yyyyMMddhhmmsszzz" );
65  // Tidy the ID up to avoid characters that may cause problems
66  // elsewhere (e.g in some parts of XML). Replaces every non-word
67  // character (word characters are the alphabet, numbers and
68  // underscore) with an underscore.
69  // Note that the first backslashe in the regular expression is
70  // there for the compiler, so the pattern is actually \W
71  mID.replace( QRegExp( "[\\W]" ), "_" );
72 
73  //set some generous defaults for scale based visibility
74  mMinScale = 0;
75  mMaxScale = 100000000;
76  mScaleBasedVisibility = false;
77  mpCacheImage = 0;
78 }
79 
80 
81 
83 {
84  delete mCRS;
85  if ( mpCacheImage )
86  {
87  delete mpCacheImage;
88  }
89 }
90 
92 {
93  return mLayerType;
94 }
95 
97 QString QgsMapLayer::id() const
98 {
99  return mID;
100 }
101 
103 void QgsMapLayer::setLayerName( const QString & name )
104 {
105  QgsDebugMsg( "new name is '" + name + "'" );
107  emit layerNameChanged();
108 }
109 
111 QString const & QgsMapLayer::name() const
112 {
113  QgsDebugMsgLevel( "returning name '" + mLayerName + "'", 3 );
114  return mLayerName;
115 }
116 
118 {
119  // Redo this every time we're asked for it, as we don't know if
120  // dataSource has changed.
121  QString safeName = QgsDataSourceURI::removePassword( mDataSource );
122  return safeName;
123 }
124 
125 QString const & QgsMapLayer::source() const
126 {
127  return mDataSource;
128 }
129 
131 {
132  return mLayerExtent;
133 }
134 
135 bool QgsMapLayer::draw( QgsRenderContext& rendererContext )
136 {
137  return false;
138 }
139 
141 {
142  // QgsDebugMsg("entered.");
143 }
144 
145 bool QgsMapLayer::readXML( QDomNode & layer_node )
146 {
148  CUSTOM_CRS_VALIDATION savedValidation;
149  bool layerError;
150 
151  QDomElement element = layer_node.toElement();
152 
153  QDomNode mnl;
154  QDomElement mne;
155 
156  // read provider
157  QString provider;
158  mnl = layer_node.namedItem( "provider" );
159  mne = mnl.toElement();
160  provider = mne.text();
161 
162  // set data source
163  mnl = layer_node.namedItem( "datasource" );
164  mne = mnl.toElement();
165  mDataSource = mne.text();
166 
167  if ( provider == "spatialite" )
168  {
170  uri.setDatabase( QgsProject::instance()->readPath( uri.database() ) );
171  mDataSource = uri.uri();
172  }
173  else if ( provider == "ogr" )
174  {
175  QStringList theURIParts = mDataSource.split( "|" );
176  theURIParts[0] = QgsProject::instance()->readPath( theURIParts[0] );
177  mDataSource = theURIParts.join( "|" );
178  }
179  else if ( provider == "delimitedtext" )
180  {
181  QUrl urlSource = QUrl::fromEncoded( mDataSource.toAscii() );
182 
183  if ( !mDataSource.startsWith( "file:" ) )
184  {
185  QUrl file = QUrl::fromLocalFile( mDataSource.left( mDataSource.indexOf( "?" ) ) );
186  urlSource.setScheme( "file" );
187  urlSource.setPath( file.path() );
188  }
189 
190  QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->readPath( urlSource.toLocalFile() ) );
191  urlDest.setQueryItems( urlSource.queryItems() );
192  mDataSource = QString::fromAscii( urlDest.toEncoded() );
193  }
194  else
195  {
197  }
198 
199  // Set the CRS from project file, asking the user if necessary.
200  // Make it the saved CRS to have WMS layer projected correctly.
201  // We will still overwrite whatever GDAL etc picks up anyway
202  // further down this function.
203  mnl = layer_node.namedItem( "layername" );
204  mne = mnl.toElement();
205 
206  QDomNode srsNode = layer_node.namedItem( "srs" );
207  mCRS->readXML( srsNode );
208  mCRS->setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
209  mCRS->validate();
210  savedCRS = *mCRS;
211 
212  // Do not validate any projections in children, they will be overwritten anyway.
213  // No need to ask the user for a projections when it is overwritten, is there?
216 
217  // now let the children grab what they need from the Dom node.
218  layerError = !readXml( layer_node );
219 
220  // overwrite CRS with what we read from project file before the raster/vector
221  // file readnig functions changed it. They will if projections is specfied in the file.
222  // FIXME: is this necessary?
224  *mCRS = savedCRS;
225 
226  // Abort if any error in layer, such as not found.
227  if ( layerError )
228  {
229  return false;
230  }
231 
232  // the internal name is just the data source basename
233  //QFileInfo dataSourceFileInfo( mDataSource );
234  //internalName = dataSourceFileInfo.baseName();
235 
236  // set ID
237  mnl = layer_node.namedItem( "id" );
238  if ( ! mnl.isNull() )
239  {
240  mne = mnl.toElement();
241  if ( ! mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
242  {
243  mID = mne.text();
244  }
245  }
246 
247  // use scale dependent visibility flag
248  toggleScaleBasedVisibility( element.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
249  setMinimumScale( element.attribute( "minimumScale" ).toFloat() );
250  setMaximumScale( element.attribute( "maximumScale" ).toFloat() );
251 
252  // set name
253  mnl = layer_node.namedItem( "layername" );
254  mne = mnl.toElement();
255  setLayerName( mne.text() );
256 
257  //read transparency level
258  QDomNode transparencyNode = layer_node.namedItem( "transparencyLevelInt" );
259  if ( ! transparencyNode.isNull() )
260  {
261  // set transparency level only if it's in project
262  // (otherwise it sets the layer transparent)
263  QDomElement myElement = transparencyNode.toElement();
264  setTransparency( myElement.text().toInt() );
265  }
266 
267  readCustomProperties( layer_node );
268 
269  return true;
270 } // void QgsMapLayer::readXML
271 
272 
273 bool QgsMapLayer::readXml( QDomNode & layer_node )
274 {
275  // NOP by default; children will over-ride with behavior specific to them
276 
277  return true;
278 } // void QgsMapLayer::readXml
279 
280 
281 
282 bool QgsMapLayer::writeXML( QDomNode & layer_node, QDomDocument & document )
283 {
284  // general layer metadata
285  QDomElement maplayer = document.createElement( "maplayer" );
286 
287  // use scale dependent visibility flag
288  maplayer.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
289  maplayer.setAttribute( "minimumScale", minimumScale() );
290  maplayer.setAttribute( "maximumScale", maximumScale() );
291 
292  // ID
293  QDomElement layerId = document.createElement( "id" );
294  QDomText layerIdText = document.createTextNode( id() );
295  layerId.appendChild( layerIdText );
296 
297  maplayer.appendChild( layerId );
298 
299  // data source
300  QDomElement dataSource = document.createElement( "datasource" );
301 
302  QString src = source();
303 
304  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
305  if ( vlayer && vlayer->providerType() == "spatialite" )
306  {
307  QgsDataSourceURI uri( src );
308  QString database = QgsProject::instance()->writePath( uri.database() );
309  uri.setConnection( uri.host(), uri.port(), database, uri.username(), uri.password() );
310  src = uri.uri();
311  }
312  else if ( vlayer && vlayer->providerType() == "ogr" )
313  {
314  QStringList theURIParts = src.split( "|" );
315  theURIParts[0] = QgsProject::instance()->writePath( theURIParts[0] );
316  src = theURIParts.join( "|" );
317  }
318  else if ( vlayer && vlayer->providerType() == "delimitedtext" )
319  {
320  QUrl urlSource = QUrl::fromEncoded( src.toAscii() );
321  QUrl urlDest = QUrl::fromLocalFile( QgsProject::instance()->writePath( urlSource.toLocalFile() ) );
322  urlDest.setQueryItems( urlSource.queryItems() );
323  src = QString::fromAscii( urlDest.toEncoded() );
324  }
325  else
326  {
327  src = QgsProject::instance()->writePath( src );
328  }
329 
330  QDomText dataSourceText = document.createTextNode( src );
331  dataSource.appendChild( dataSourceText );
332 
333  maplayer.appendChild( dataSource );
334 
335 
336  // layer name
337  QDomElement layerName = document.createElement( "layername" );
338  QDomText layerNameText = document.createTextNode( name() );
339  layerName.appendChild( layerNameText );
340 
341  maplayer.appendChild( layerName );
342 
343  // timestamp if supported
344  if ( timestamp() > QDateTime() )
345  {
346  QDomElement stamp = document.createElement( "timestamp" );
347  QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
348  stamp.appendChild( stampText );
349  maplayer.appendChild( stamp );
350  }
351 
352  maplayer.appendChild( layerName );
353 
354  // zorder
355  // This is no longer stored in the project file. It is superfluous since the layers
356  // are written and read in the proper order.
357 
358  // spatial reference system id
359  QDomElement mySrsElement = document.createElement( "srs" );
360  mCRS->writeXML( mySrsElement, document );
361  maplayer.appendChild( mySrsElement );
362 
363  // <transparencyLevelInt>
364  QDomElement transparencyLevelIntElement = document.createElement( "transparencyLevelInt" );
365  QDomText transparencyLevelIntText = document.createTextNode( QString::number( getTransparency() ) );
366  transparencyLevelIntElement.appendChild( transparencyLevelIntText );
367  maplayer.appendChild( transparencyLevelIntElement );
368  // now append layer node to map layer node
369 
370  layer_node.appendChild( maplayer );
371 
372  writeCustomProperties( maplayer, document );
373 
374  return writeXml( maplayer, document );
375 
376 } // bool QgsMapLayer::writeXML
377 
378 
379 
380 bool QgsMapLayer::writeXml( QDomNode & layer_node, QDomDocument & document )
381 {
382  // NOP by default; children will over-ride with behavior specific to them
383 
384  return true;
385 } // void QgsMapLayer::writeXml
386 
387 
388 
389 
391 {
392  return mValid;
393 }
394 
395 
397 {
398  QgsDebugMsg( "called" );
399  // TODO: emit a signal - it will be used to update legend
400 }
401 
402 
404 {
405  return QString();
406 }
407 
409 {
410  return QString();
411 }
412 
413 void QgsMapLayer::connectNotify( const char * signal )
414 {
415  QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
416 } // QgsMapLayer::connectNotify
417 
418 
419 
420 void QgsMapLayer::toggleScaleBasedVisibility( bool theVisibilityFlag )
421 {
422  mScaleBasedVisibility = theVisibilityFlag;
423 }
424 
426 {
427  return mScaleBasedVisibility;
428 }
429 
430 void QgsMapLayer::setMinimumScale( float theMinScale )
431 {
432  mMinScale = theMinScale;
433 }
434 
436 {
437  return mMinScale;
438 }
439 
440 
441 void QgsMapLayer::setMaximumScale( float theMaxScale )
442 {
443  mMaxScale = theMaxScale;
444 }
445 
447 {
448  return mMaxScale;
449 }
450 
451 
453 {
454  return QStringList(); // Empty
455 }
456 
457 void QgsMapLayer::setLayerOrder( QStringList layers )
458 {
459  // NOOP
460 }
461 
462 void QgsMapLayer::setSubLayerVisibility( QString name, bool vis )
463 {
464  // NOOP
465 }
466 
468 {
469  return *mCRS;
470 }
471 
473 {
474  // This will be dropped in QGIS 2.0 due to conflicting name
475  // Please use crs() in the future
476  return *mCRS;
477 }
478 
479 void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem& srs, bool emitSignal )
480 {
481  *mCRS = srs;
482  if ( emitSignal )
483  emit layerCrsChanged();
484 }
485 
487 {
488  return mTransparencyLevel;
489 }
490 
491 void QgsMapLayer::setTransparency( unsigned int theInt )
492 {
493  mTransparencyLevel = theInt;
494 }
495 
496 QString QgsMapLayer::capitaliseLayerName( const QString name )
497 {
498  // Capitalise the first letter of the layer name if requested
499  QSettings settings;
500  bool capitaliseLayerName =
501  settings.value( "qgis/capitaliseLayerName", QVariant( false ) ).toBool();
502 
503  QString layerName( name );
504 
505  if ( capitaliseLayerName )
506  layerName = layerName.left( 1 ).toUpper() + layerName.mid( 1 );
507 
508  return layerName;
509 }
510 
511 QString QgsMapLayer::loadDefaultStyle( bool & theResultFlag )
512 {
513  QString myURI = publicSource();
514  QFileInfo myFileInfo( myURI );
515  QString key;
516  if ( myFileInfo.exists() )
517  {
518  // get the file name for our .qml style file
519  key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
520  }
521  else
522  {
523  key = myURI;
524  }
525  return loadNamedStyle( key, theResultFlag );
526 }
527 
528 bool QgsMapLayer::loadNamedStyleFromDb( const QString db, const QString theURI, QString &qml )
529 {
530  bool theResultFlag = false;
531 
532  // read from database
533  sqlite3 *myDatabase;
534  sqlite3_stmt *myPreparedStatement;
535  const char *myTail;
536  int myResult;
537 
538  QgsDebugMsg( QString( "Trying to load style for \"%1\" from \"%2\"" ).arg( theURI ).arg( db ) );
539 
540  if ( !QFile( db ).exists() )
541  return false;
542 
543  myResult = sqlite3_open( db.toUtf8().data(), &myDatabase );
544  if ( myResult != SQLITE_OK )
545  {
546  return false;
547  }
548 
549  QString mySql = "select qml from tbl_styles where style=?";
550  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
551  if ( myResult == SQLITE_OK )
552  {
553  QByteArray param = theURI.toUtf8();
554 
555  if ( sqlite3_bind_text( myPreparedStatement, 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
556  sqlite3_step( myPreparedStatement ) == SQLITE_ROW )
557  {
558  qml = QString::fromUtf8(( char * )sqlite3_column_text( myPreparedStatement, 0 ) );
559  theResultFlag = true;
560  }
561 
562  sqlite3_finalize( myPreparedStatement );
563  }
564 
565  sqlite3_close( myDatabase );
566 
567  return theResultFlag;
568 }
569 
570 QString QgsMapLayer::loadNamedStyle( const QString theURI, bool &theResultFlag )
571 {
572  theResultFlag = false;
573 
574  QDomDocument myDocument( "qgis" );
575 
576  // location of problem associated with errorMsg
577  int line, column;
578  QString myErrorMessage;
579 
580  QFile myFile( theURI );
581  if ( myFile.open( QFile::ReadOnly ) )
582  {
583  // read file
584  theResultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
585  if ( !theResultFlag )
586  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
587  myFile.close();
588  }
589  else
590  {
591  QFileInfo project( QgsProject::instance()->fileName() );
592  QgsDebugMsg( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ) );
593 
594  QString qml;
595  if ( loadNamedStyleFromDb( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ), theURI, qml ) ||
596  ( project.exists() && loadNamedStyleFromDb( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), theURI, qml ) ) ||
597  loadNamedStyleFromDb( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( "resources/qgis.qmldb" ), theURI, qml ) )
598  {
599  theResultFlag = myDocument.setContent( qml, &myErrorMessage, &line, &column );
600  if ( !theResultFlag )
601  {
602  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
603  }
604  }
605  else
606  {
607  myErrorMessage = tr( "style not found in database" );
608  }
609  }
610 
611  if ( !theResultFlag )
612  {
613  return myErrorMessage;
614  }
615 
616  // now get the layer node out and pass it over to the layer
617  // to deserialise...
618  QDomElement myRoot = myDocument.firstChildElement( "qgis" );
619  if ( myRoot.isNull() )
620  {
621  myErrorMessage = tr( "Error: qgis element could not be found in %1" ).arg( theURI );
622  theResultFlag = false;
623  return myErrorMessage;
624  }
625 
626  // use scale dependent visibility flag
627  toggleScaleBasedVisibility( myRoot.attribute( "hasScaleBasedVisibilityFlag" ).toInt() == 1 );
628  setMinimumScale( myRoot.attribute( "minimumScale" ).toFloat() );
629  setMaximumScale( myRoot.attribute( "maximumScale" ).toFloat() );
630 
631  //read transparency level
632  QDomNode transparencyNode = myRoot.namedItem( "transparencyLevelInt" );
633  if ( ! transparencyNode.isNull() )
634  {
635  // set transparency level only if it's in project
636  // (otherwise it sets the layer transparent)
637  QDomElement myElement = transparencyNode.toElement();
638  setTransparency( myElement.text().toInt() );
639  }
640 
641  QString errorMsg;
642  theResultFlag = readSymbology( myRoot, errorMsg );
643  if ( !theResultFlag )
644  {
645  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( theURI ).arg( errorMsg );
646  return myErrorMessage;
647  }
648 
649  return "";
650 }
651 
652 QString QgsMapLayer::saveDefaultStyle( bool & theResultFlag )
653 {
654  return saveNamedStyle( publicSource(), theResultFlag );
655 }
656 
657 QString QgsMapLayer::saveNamedStyle( const QString theURI, bool & theResultFlag )
658 {
659  QString myErrorMessage;
660 
661  QDomImplementation DomImplementation;
662  QDomDocumentType documentType =
663  DomImplementation.createDocumentType(
664  "qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" );
665  QDomDocument myDocument( documentType );
666  QDomElement myRootNode = myDocument.createElement( "qgis" );
667  myRootNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
668  myDocument.appendChild( myRootNode );
669 
670  // use scale dependent visibility flag
671  myRootNode.setAttribute( "hasScaleBasedVisibilityFlag", hasScaleBasedVisibility() ? 1 : 0 );
672  myRootNode.setAttribute( "minimumScale", minimumScale() );
673  myRootNode.setAttribute( "maximumScale", maximumScale() );
674 
675  // <transparencyLevelInt>
676  QDomElement transparencyLevelIntElement = myDocument.createElement( "transparencyLevelInt" );
677  QDomText transparencyLevelIntText = myDocument.createTextNode( QString::number( getTransparency() ) );
678  transparencyLevelIntElement.appendChild( transparencyLevelIntText );
679  myRootNode.appendChild( transparencyLevelIntElement );
680  // now append layer node to map layer node
681 
682  QString errorMsg;
683  if ( !writeSymbology( myRootNode, myDocument, errorMsg ) )
684  {
685  return tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
686  }
687 
688  // check if the uri is a file or ends with .qml,
689  // which indicates that it should become one
690  // everything else goes to the database
691  QString filename;
692 
693  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
694  if ( vlayer && vlayer->providerType() == "ogr" )
695  {
696  QStringList theURIParts = theURI.split( "|" );
697  filename = theURIParts[0];
698  }
699  else if ( vlayer && vlayer->providerType() == "delimitedtext" )
700  {
701  filename = QUrl::fromEncoded( theURI.toAscii() ).toLocalFile();
702  }
703  else
704  {
705  filename = theURI;
706  }
707 
708  QFileInfo myFileInfo( filename );
709  if ( myFileInfo.exists() || filename.endsWith( ".qml", Qt::CaseInsensitive ) )
710  {
711  QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
712  if ( !myDirInfo.isWritable() )
713  {
714  return tr( "The directory containing your dataset needs to be writeable!" );
715  }
716 
717  // now construct the file name for our .qml style file
718  QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
719 
720  QFile myFile( myFileName );
721  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
722  {
723  QTextStream myFileStream( &myFile );
724  // save as utf-8 with 2 spaces for indents
725  myDocument.save( myFileStream, 2 );
726  myFile.close();
727  theResultFlag = true;
728  return tr( "Created default style file as %1" ).arg( myFileName );
729  }
730  else
731  {
732  theResultFlag = false;
733  return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
734  }
735  }
736  else
737  {
738  QString qml = myDocument.toString();
739 
740  // read from database
741  sqlite3 *myDatabase;
742  sqlite3_stmt *myPreparedStatement;
743  const char *myTail;
744  int myResult;
745 
746  myResult = sqlite3_open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( "qgis.qmldb" ).toUtf8().data(), &myDatabase );
747  if ( myResult != SQLITE_OK )
748  {
749  return tr( "User database could not be opened." );
750  }
751 
752  QByteArray param0 = theURI.toUtf8();
753  QByteArray param1 = qml.toUtf8();
754 
755  QString mySql = "create table if not exists tbl_styles(style varchar primary key,qml varchar)";
756  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
757  if ( myResult == SQLITE_OK )
758  {
759  if ( sqlite3_step( myPreparedStatement ) != SQLITE_DONE )
760  {
761  sqlite3_finalize( myPreparedStatement );
762  sqlite3_close( myDatabase );
763  theResultFlag = false;
764  return tr( "The style table could not be created." );
765  }
766  }
767 
768  sqlite3_finalize( myPreparedStatement );
769 
770  mySql = "insert into tbl_styles(style,qml) values (?,?)";
771  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
772  if ( myResult == SQLITE_OK )
773  {
774  if ( sqlite3_bind_text( myPreparedStatement, 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
775  sqlite3_bind_text( myPreparedStatement, 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
776  sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
777  {
778  theResultFlag = true;
779  myErrorMessage = tr( "The style %1 was saved to database" ).arg( theURI );
780  }
781  }
782 
783  sqlite3_finalize( myPreparedStatement );
784 
785  if ( !theResultFlag )
786  {
787  QString mySql = "update tbl_styles set qml=? where style=?";
788  myResult = sqlite3_prepare( myDatabase, mySql.toUtf8().data(), mySql.toUtf8().length(), &myPreparedStatement, &myTail );
789  if ( myResult == SQLITE_OK )
790  {
791  if ( sqlite3_bind_text( myPreparedStatement, 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
792  sqlite3_bind_text( myPreparedStatement, 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
793  sqlite3_step( myPreparedStatement ) == SQLITE_DONE )
794  {
795  theResultFlag = true;
796  myErrorMessage = tr( "The style %1 was updated in the database." ).arg( theURI );
797  }
798  else
799  {
800  theResultFlag = false;
801  myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( theURI );
802  }
803  }
804  else
805  {
806  theResultFlag = false;
807  myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( theURI );
808  }
809 
810  sqlite3_finalize( myPreparedStatement );
811  }
812 
813  sqlite3_close( myDatabase );
814  }
815 
816  return myErrorMessage;
817 }
818 
819 
820 
821 
823 {
824  return &mUndoStack;
825 }
826 
827 
828 void QgsMapLayer::setCustomProperty( const QString& key, const QVariant& value )
829 {
830  mCustomProperties[key] = value;
831 }
832 
833 QVariant QgsMapLayer::customProperty( const QString& value, const QVariant& defaultValue ) const
834 {
835  return mCustomProperties.value( value, defaultValue );
836 }
837 
838 void QgsMapLayer::removeCustomProperty( const QString& key )
839 {
840  mCustomProperties.remove( key );
841 }
842 
843 void QgsMapLayer::readCustomProperties( const QDomNode& layerNode, const QString& keyStartsWith )
844 {
845  QDomNode propsNode = layerNode.namedItem( "customproperties" );
846  if ( propsNode.isNull() ) // no properties stored...
847  return;
848 
849  if ( !keyStartsWith.isEmpty() )
850  {
851  //remove old keys
852  QStringList keysToRemove;
853  QMap<QString, QVariant>::const_iterator pIt = mCustomProperties.constBegin();
854  for ( ; pIt != mCustomProperties.constEnd(); ++pIt )
855  {
856  if ( pIt.key().startsWith( keyStartsWith ) )
857  {
858  keysToRemove.push_back( pIt.key() );
859  }
860  }
861 
862  QStringList::const_iterator sIt = keysToRemove.constBegin();
863  for ( ; sIt != keysToRemove.constEnd(); ++sIt )
864  {
865  mCustomProperties.remove( *sIt );
866  }
867  }
868  else
869  {
870  mCustomProperties.clear();
871  }
872 
873  QDomNodeList nodes = propsNode.childNodes();
874 
875  for ( int i = 0; i < nodes.size(); i++ )
876  {
877  QDomNode propNode = nodes.at( i );
878  if ( propNode.isNull() || propNode.nodeName() != "property" )
879  continue;
880  QDomElement propElement = propNode.toElement();
881 
882  QString key = propElement.attribute( "key" );
883  if ( key.isEmpty() || key.startsWith( keyStartsWith ) )
884  {
885  QString value = propElement.attribute( "value" );
886  mCustomProperties[key] = QVariant( value );
887  }
888  }
889 
890 }
891 
892 void QgsMapLayer::writeCustomProperties( QDomNode & layerNode, QDomDocument & doc ) const
893 {
894  //remove already existing <customproperties> tags
895  QDomNodeList propertyList = layerNode.toElement().elementsByTagName( "customproperties" );
896  for ( int i = 0; i < propertyList.size(); ++i )
897  {
898  layerNode.removeChild( propertyList.at( i ) );
899  }
900 
901  QDomElement propsElement = doc.createElement( "customproperties" );
902 
903  for ( QMap<QString, QVariant>::const_iterator it = mCustomProperties.constBegin(); it != mCustomProperties.constEnd(); ++it )
904  {
905  QDomElement propElement = doc.createElement( "property" );
906  propElement.setAttribute( "key", it.key() );
907  propElement.setAttribute( "value", it.value().toString() );
908  propsElement.appendChild( propElement );
909  }
910 
911  layerNode.appendChild( propsElement );
912 }
913 
914 void QgsMapLayer::setCacheImage( QImage * thepImage )
915 {
916  QgsDebugMsg( "cache Image set!" );
917  if ( mpCacheImage == thepImage )
918  return;
919 
920  if ( mpCacheImage )
921  {
922  delete mpCacheImage;
923  }
924  mpCacheImage = thepImage;
925 }
926 
928 {
929  return false;
930 }
931 
932 void QgsMapLayer::setValid( bool valid )
933 {
934  mValid = valid;
935 }
936 
938 {
939  setCacheImage( 0 );
940 }