QGIS API Documentation 3.27.0-Master (1d7a28cfd2)
qgsbookmarkmanager.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsbookmarkmanager.cpp
3 --------------------
4 Date : September 2019
5 Copyright : (C) 2019 Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16#include "qgsbookmarkmanager.h"
17#include "qgsproject.h"
18#include "qgssettings.h"
19#include "qgssqliteutils.h"
20#include "qgsapplication.h"
21#include <QUuid>
22#include <QTextStream>
23#include <sqlite3.h>
24
25//
26// QgsBookMark
27//
28
29QString QgsBookmark::id() const
30{
31 return mId;
32}
33
34void QgsBookmark::setId( const QString &id )
35{
36 mId = id;
37}
38
39QgsBookmark QgsBookmark::fromXml( const QDomElement &element, const QDomDocument & )
40{
42 b.setId( element.attribute( QStringLiteral( "id" ) ) );
43 b.setName( element.attribute( QStringLiteral( "name" ) ) );
44 b.setGroup( element.attribute( QStringLiteral( "group" ) ) );
45 const QgsRectangle e = QgsRectangle::fromWkt( element.attribute( QStringLiteral( "extent" ) ) );
47 crs.readXml( element );
49 return b;
50}
51
52QDomElement QgsBookmark::writeXml( QDomDocument &doc ) const
53{
54 QDomElement bookmarkElem = doc.createElement( QStringLiteral( "Bookmark" ) );
55 bookmarkElem.setAttribute( QStringLiteral( "id" ), mId );
56 bookmarkElem.setAttribute( QStringLiteral( "name" ), mName );
57 bookmarkElem.setAttribute( QStringLiteral( "group" ), mGroup );
58 bookmarkElem.setAttribute( QStringLiteral( "extent" ), mExtent.asWktPolygon() );
59 mExtent.crs().writeXml( bookmarkElem, doc );
60 return bookmarkElem;
61}
62
63bool QgsBookmark::operator==( const QgsBookmark &other ) const
64{
65 return mId == other.mId && mName == other.mName && mExtent == other.mExtent && mGroup == other.mGroup;
66}
67
68bool QgsBookmark::operator!=( const QgsBookmark &other ) const
69{
70 return !( *this == other );
71}
72
73QString QgsBookmark::name() const
74{
75 return mName;
76}
77
78void QgsBookmark::setName( const QString &name )
79{
80 mName = name;
81}
82
83QString QgsBookmark::group() const
84{
85 return mGroup;
86}
87
88void QgsBookmark::setGroup( const QString &group )
89{
90 mGroup = group;
91}
92
94{
95 return mExtent;
96}
97
99{
100 mExtent = extent;
101}
102
103
104//
105// QgsBookmarkManager
106//
107
109{
110 QgsBookmarkManager *res = new QgsBookmarkManager( project );
111 res->mProject = project;
112 return res;
113}
114
116 : QObject( parent )
117{
118 // we defer actually loading bookmarks until initialize() is called..
119}
120
122{
123 store();
124}
125
126QString QgsBookmarkManager::addBookmark( const QgsBookmark &b, bool *ok )
127{
128 if ( ok )
129 *ok = false;
130
131 QgsBookmark bookmark = b;
132 if ( bookmark.id().isEmpty() )
133 bookmark.setId( QUuid::createUuid().toString() );
134 else
135 {
136 // check for duplicate ID
137 for ( const QgsBookmark &b : std::as_const( mBookmarks ) )
138 {
139 if ( b.id() == bookmark.id() )
140 {
141 return QString();
142 }
143 }
144 }
145
146 if ( ok )
147 *ok = true;
148
149 emit bookmarkAboutToBeAdded( bookmark.id() );
150 mBookmarks << bookmark;
151 if ( !mGroups.contains( bookmark.group() ) )
152 mGroups << bookmark.group();
153 emit bookmarkAdded( bookmark.id() );
154 if ( mProject )
155 {
156 mProject->setDirty( true );
157 }
158
159 return bookmark.id();
160}
161
162bool QgsBookmarkManager::removeBookmark( const QString &id )
163{
164 if ( id.isEmpty() )
165 return false;
166
167 QString group;
168 int pos = -1;
169 int i = 0;
170 for ( const QgsBookmark &b : std::as_const( mBookmarks ) )
171 {
172 if ( b.id() == id )
173 {
174 group = b.group();
175 pos = i;
176 break;
177 }
178 i++;
179 }
180
181 if ( pos < 0 )
182 return false;
183
184 emit bookmarkAboutToBeRemoved( id );
185 mBookmarks.removeAt( pos );
186 if ( bookmarksByGroup( group ).isEmpty() )
187 mGroups.removeOne( group );
188 emit bookmarkRemoved( id );
189 if ( mProject )
190 {
191 mProject->setDirty( true );
192 }
193
194 return true;
195}
196
198{
199 // check for duplicate ID
200 int i = 0;
201 for ( const QgsBookmark &b : std::as_const( mBookmarks ) )
202 {
203 if ( b.id() == bookmark.id() )
204 {
205 if ( mBookmarks[i].group() != bookmark.group() )
206 {
207 if ( bookmarksByGroup( mBookmarks[i].group() ).count() == 1 )
208 mGroups.removeOne( mBookmarks[i].group() );
209 if ( !mGroups.contains( bookmark.group() ) )
210 mGroups << bookmark.group();
211 }
212 mBookmarks[i] = bookmark;
213 emit bookmarkChanged( bookmark.id() );
214 if ( mProject )
215 {
216 mProject->setDirty( true );
217 }
218
219 return true;
220 }
221 i++;
222 }
223 return false;
224}
225
227{
228 const QList< QgsBookmark > bookmarks = mBookmarks;
229 for ( const QgsBookmark &b : bookmarks )
230 {
231 removeBookmark( b.id() );
232 }
233}
234
235QStringList QgsBookmarkManager::groups() const
236{
237 return mGroups;
238}
239
240void QgsBookmarkManager::renameGroup( const QString &oldName, const QString &newName )
241{
242 for ( int i = 0; i < mBookmarks.count(); ++i )
243 {
244 if ( mBookmarks.at( i ).group() == oldName )
245 {
246 mBookmarks[ i ].setGroup( newName );
247 emit bookmarkChanged( mBookmarks.at( i ).id() );
248 }
249 }
250}
251
252QList<QgsBookmark> QgsBookmarkManager::bookmarks() const
253{
254 return mBookmarks;
255}
256
258{
259 for ( const QgsBookmark &b : mBookmarks )
260 {
261 if ( b.id() == id )
262 return b;
263 }
264 return QgsBookmark();
265}
266
267QList<QgsBookmark> QgsBookmarkManager::bookmarksByGroup( const QString &group )
268{
269 QList<QgsBookmark> bookmarks;
270 for ( const QgsBookmark &b : mBookmarks )
271 {
272 if ( b.group() == group )
273 bookmarks << b;
274 }
275 return bookmarks;
276}
277
278bool QgsBookmarkManager::readXml( const QDomElement &element, const QDomDocument &doc )
279{
280 clear();
281
282 QDomElement bookmarksElem = element;
283 if ( element.tagName() != QLatin1String( "Bookmarks" ) )
284 {
285 bookmarksElem = element.firstChildElement( QStringLiteral( "Bookmarks" ) );
286 }
287 bool result = true;
288 if ( mProject && bookmarksElem.isNull() )
289 {
290 // handle legacy projects
291 const int count = mProject->readNumEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/count" ) );
292 for ( int i = 0; i < count; ++i )
293 {
294 const double minX = mProject->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinX" ).arg( i ) );
295 const double minY = mProject->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinY" ).arg( i ) );
296 const double maxX = mProject->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MaxX" ).arg( i ) );
297 const double maxY = mProject->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MaxY" ).arg( i ) );
298 const long srid = mProject->readNumEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/SRID" ).arg( i ) );
299 QgsBookmark b;
300 b.setId( QStringLiteral( "bookmark_%1" ).arg( i ) );
301 b.setName( mProject->readEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Name" ).arg( i ) ) );
303
304 bool added = false;
305 addBookmark( b, &added );
306 result = added && result;
307 }
308 return result;
309 }
310
311 //restore each
312 QDomNodeList bookmarkNodes = element.elementsByTagName( QStringLiteral( "Bookmark" ) );
313 for ( int i = 0; i < bookmarkNodes.size(); ++i )
314 {
315 QgsBookmark b = QgsBookmark::fromXml( bookmarkNodes.at( i ).toElement(), doc );
316 bool added = false;
317 addBookmark( b, &added );
318 result = added && result;
319 }
320
321 return result;
322}
323
324QDomElement QgsBookmarkManager::writeXml( QDomDocument &doc ) const
325{
326 QDomElement bookmarksElem = doc.createElement( QStringLiteral( "Bookmarks" ) );
327
328 for ( const QgsBookmark &b : mBookmarks )
329 {
330 QDomElement bookmarkElem = b.writeXml( doc );
331 bookmarksElem.appendChild( bookmarkElem );
332 }
333 return bookmarksElem;
334}
335
336bool QgsBookmarkManager::moveBookmark( const QString &id, QgsBookmarkManager *destination )
337{
338 QgsBookmark b = bookmarkById( id );
339 if ( b.id().isEmpty() )
340 return false;
341
342 removeBookmark( id );
343 bool ok = false;
344 destination->addBookmark( b, &ok );
345 return ok;
346}
347
348bool QgsBookmarkManager::exportToFile( const QString &path, const QList<const QgsBookmarkManager *> &managers, const QString &group )
349{
350 // note - we don't use the other writeXml implementation, to maintain older format compatibility
351 QDomDocument doc( QStringLiteral( "qgis_bookmarks" ) );
352 QDomElement root = doc.createElement( QStringLiteral( "qgis_bookmarks" ) );
353 doc.appendChild( root );
354
355 QList<QString> headerList;
356 headerList
357 << QStringLiteral( "project" )
358 << QStringLiteral( "xmin" )
359 << QStringLiteral( "ymin" )
360 << QStringLiteral( "xmax" )
361 << QStringLiteral( "ymax" )
362 << QStringLiteral( "sr_id" );
363
364 for ( const QgsBookmarkManager *manager : managers )
365 {
366 const QList< QgsBookmark > bookmarks = manager->bookmarks();
367 for ( const QgsBookmark &b : bookmarks )
368 {
369 if ( !group.isEmpty() && b.group() != group )
370 continue;
371
372 QDomElement bookmark = doc.createElement( QStringLiteral( "bookmark" ) );
373 root.appendChild( bookmark );
374
375 QDomElement id = doc.createElement( QStringLiteral( "id" ) );
376 id.appendChild( doc.createTextNode( b.id() ) );
377 bookmark.appendChild( id );
378
379 QDomElement name = doc.createElement( QStringLiteral( "name" ) );
380 name.appendChild( doc.createTextNode( b.name() ) );
381 bookmark.appendChild( name );
382
383 QDomElement group = doc.createElement( QStringLiteral( "project" ) );
384 group.appendChild( doc.createTextNode( b.group() ) );
385 bookmark.appendChild( group );
386
387 QDomElement xMin = doc.createElement( QStringLiteral( "xmin" ) );
388 xMin.appendChild( doc.createTextNode( qgsDoubleToString( b.extent().xMinimum() ) ) );
389 bookmark.appendChild( xMin );
390 QDomElement yMin = doc.createElement( QStringLiteral( "ymin" ) );
391 yMin.appendChild( doc.createTextNode( qgsDoubleToString( b.extent().yMinimum() ) ) );
392 bookmark.appendChild( yMin );
393 QDomElement xMax = doc.createElement( QStringLiteral( "xmax" ) );
394 xMax.appendChild( doc.createTextNode( qgsDoubleToString( b.extent().xMaximum() ) ) );
395 bookmark.appendChild( xMax );
396 QDomElement yMax = doc.createElement( QStringLiteral( "ymax" ) );
397 yMax.appendChild( doc.createTextNode( qgsDoubleToString( b.extent().yMaximum() ) ) );
398 bookmark.appendChild( yMax );
399
400 QDomElement crs = doc.createElement( QStringLiteral( "sr_id" ) );
401 crs.appendChild( doc.createTextNode( QString::number( b.extent().crs().srsid() ) ) );
402 bookmark.appendChild( crs );
403 }
404 }
405
406 QFile f( path );
407 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
408 {
409 f.close();
410 return false;
411 }
412
413 QTextStream out( &f );
414#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
415 out.setCodec( "UTF-8" );
416#endif
417 doc.save( out, 2 );
418 f.close();
419
420 return true;
421}
422
423bool QgsBookmarkManager::importFromFile( const QString &path )
424{
425 if ( path.isEmpty() )
426 {
427 return false;
428 }
429
430 QFile f( path );
431 if ( !f.open( QIODevice::ReadOnly | QIODevice::Text ) )
432 {
433 return false;
434 }
435
436 QDomDocument doc;
437 if ( !doc.setContent( &f ) )
438 {
439 return false;
440 }
441 f.close();
442
443 QDomElement docElem = doc.documentElement();
444 QDomNodeList nodeList = docElem.elementsByTagName( QStringLiteral( "bookmark" ) );
445
446 bool res = true;
447 for ( int i = 0; i < nodeList.count(); i++ )
448 {
449 QDomNode bookmark = nodeList.at( i );
450 QDomElement name = bookmark.firstChildElement( QStringLiteral( "name" ) );
451 QDomElement prjname = bookmark.firstChildElement( QStringLiteral( "project" ) );
452 QDomElement xmin = bookmark.firstChildElement( QStringLiteral( "xmin" ) );
453 QDomElement ymin = bookmark.firstChildElement( QStringLiteral( "ymin" ) );
454 QDomElement xmax = bookmark.firstChildElement( QStringLiteral( "xmax" ) );
455 QDomElement ymax = bookmark.firstChildElement( QStringLiteral( "ymax" ) );
456 QDomElement srid = bookmark.firstChildElement( QStringLiteral( "sr_id" ) );
457
458 bool ok = false;
459 QgsBookmark b;
460 b.setName( name.text() );
461 b.setGroup( prjname.text() );
463 crs.createFromSrsId( srid.text().toLongLong() );
464 b.setExtent( QgsReferencedRectangle( QgsRectangle( xmin.text().toDouble(),
465 ymin.text().toDouble(),
466 xmax.text().toDouble(),
467 ymax.text().toDouble() ), crs ) );
468 addBookmark( b, &ok );
469 res = res && ok;
470 }
471
472 return res;
473}
474
475void QgsBookmarkManager::store()
476{
477 if ( !mFilePath.isEmpty() )
478 {
479 QFile f( mFilePath );
480 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
481 {
482 f.close();
483 return;
484 }
485
486 QDomDocument doc;
487 QDomElement elem = writeXml( doc );
488 doc.appendChild( elem );
489
490 QTextStream out( &f );
491#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
492 out.setCodec( "UTF-8" );
493#endif
494 doc.save( out, 2 );
495 f.close();
496 }
497}
498
499void QgsBookmarkManager::initialize( const QString &filePath )
500{
501 if ( mInitialized )
502 return;
503
504 mFilePath = filePath;
505
506 mInitialized = true;
507
508 // restore state
509 if ( !QFileInfo::exists( mFilePath ) )
510 {
511 //convert old bookmarks from db
513 int result = database.open( QgsApplication::qgisUserDatabaseFilePath() );
514 if ( result != SQLITE_OK )
515 {
516 return;
517 }
518
519 sqlite3_statement_unique_ptr preparedStatement = database.prepare( QStringLiteral( "SELECT name,xmin,ymin,xmax,ymax,projection_srid FROM tbl_bookmarks" ), result );
520 if ( result == SQLITE_OK )
521 {
522 while ( preparedStatement.step() == SQLITE_ROW )
523 {
524 const QString name = preparedStatement.columnAsText( 0 );
525 const double xMin = preparedStatement.columnAsDouble( 1 );
526 const double yMin = preparedStatement.columnAsDouble( 2 );
527 const double xMax = preparedStatement.columnAsDouble( 3 );
528 const double yMax = preparedStatement.columnAsDouble( 4 );
529 const long long srid = preparedStatement.columnAsInt64( 5 );
530
531 QgsBookmark b;
532 b.setName( name );
533 const QgsRectangle extent( xMin, yMin, xMax, yMax );
535 addBookmark( b );
536 }
537 }
538 store();
539 }
540 else
541 {
542 QFile f( mFilePath );
543 if ( !f.open( QIODevice::ReadOnly | QIODevice::Text ) )
544 {
545 return;
546 }
547
548 QDomDocument doc;
549 if ( !doc.setContent( &f ) )
550 {
551 return;
552 }
553 f.close();
554
555 QDomElement elem = doc.documentElement();
556 readXml( elem, doc );
557 }
558}
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
Manages storage of a set of bookmarks.
void renameGroup(const QString &oldName, const QString &newName)
Renames an existing group from oldName to newName.
bool removeBookmark(const QString &id)
Removes the bookmark with matching id from the manager.
QList< QgsBookmark > bookmarksByGroup(const QString &group)
Returns a list of bookmark with a matching group, or an empty list if no matching bookmarks were foun...
void initialize(const QString &filePath)
Initializes the bookmark manager.
void bookmarkAboutToBeRemoved(const QString &id)
Emitted when a bookmark is about to be removed from the manager.
void bookmarkChanged(const QString &id)
Emitted when a bookmark is changed.
static QgsBookmarkManager * createProjectBasedManager(QgsProject *project)
Returns a newly created QgsBookmarkManager using a project-based bookmark store, linked to the specif...
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager's state from a DOM element, restoring all bookmarks present in the XML document.
void clear()
Removes and deletes all bookmarks from the manager.
void bookmarkAdded(const QString &id)
Emitted when a bookmark has been added to the manager.
bool updateBookmark(const QgsBookmark &bookmark)
Updates the definition of a bookmark in the manager.
void bookmarkAboutToBeAdded(const QString &id)
Emitted when a bookmark is about to be added to the manager.
bool moveBookmark(const QString &id, QgsBookmarkManager *destination)
Moves the bookmark with matching id from this manager to a destination manager.
static bool exportToFile(const QString &path, const QList< const QgsBookmarkManager * > &managers, const QString &group=QString())
Exports all bookmarks from a list of managers to an xml file at the specified path.
QgsBookmark bookmarkById(const QString &id) const
Returns the bookmark with a matching id, or an empty bookmark if no matching bookmarks were found.
QStringList groups() const
Returns a list of all bookmark groups contained in the manager.
QString addBookmark(const QgsBookmark &bookmark, bool *ok=nullptr)
Adds a bookmark to the manager.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
QgsBookmarkManager(QObject *parent=nullptr)
Constructor for QgsBookmarkManager, with the specified parent object.
QList< QgsBookmark > bookmarks() const
Returns a list of all bookmarks contained in the manager.
void bookmarkRemoved(const QString &id)
Emitted when a bookmark was removed from the manager.
bool importFromFile(const QString &path)
Imports the bookmarks from an xml file at the specified path.
Represents a spatial bookmark, with a name, CRS and extent.
static QgsBookmark fromXml(const QDomElement &element, const QDomDocument &doc)
Creates a bookmark using the properties from a DOM element.
void setGroup(const QString &group)
Sets the bookmark's group, which is a user-visible string identifying the bookmark's category.
bool operator!=(const QgsBookmark &other) const
QString id() const
Returns the bookmark's unique ID.
QgsReferencedRectangle extent() const
Returns the bookmark's spatial extent.
void setExtent(const QgsReferencedRectangle &extent)
Sets the bookmark's spatial extent.
void setId(const QString &id)
Sets the bookmark's unique id.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the bookmark's properties.
bool operator==(const QgsBookmark &other) const
QString group() const
Returns the bookmark's group, which is a user-visible string identifying the bookmark's category.
void setName(const QString &name)
Sets the bookmark's name, which is a user-visible string identifying the bookmark.
QString name() const
Returns the bookmark's name, which is a user-visible string identifying the bookmark.
This class represents a coordinate reference system (CRS).
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
bool createFromSrsId(long srsId)
Sets this CRS by lookup of internal QGIS CRS ID in the CRS database.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
Reads an integer from the specified scope and key.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Reads a string from the specified scope and key.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
Reads a double from the specified scope and key.
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:573
A rectangle specified with double values.
Definition: qgsrectangle.h:42
static QgsRectangle fromWkt(const QString &wkt)
Creates a new rectangle from a wkt string.
QString asWktPolygon() const
Returns a string representation of the rectangle as a WKT Polygon.
QgsCoordinateReferenceSystem crs() const
Returns the associated coordinate reference system, or an invalid CRS if no reference system is set.
A QgsRectangle with associated coordinate reference system.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
int open(const QString &path)
Opens the database at the specified file path.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
QString columnAsText(int column) const
Returns the column value from the current statement row as a string.
double columnAsDouble(int column) const
Gets column value from the current statement row as a double.
int step()
Steps to the next record in the statement, returning the sqlite3 result code.
qlonglong columnAsInt64(int column) const
Gets column value from the current statement row as a long long integer (64 bits).
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:2446
const QgsCoordinateReferenceSystem & crs