QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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
18#include <sqlite3.h>
19
20#include "qgsapplication.h"
21#include "qgsproject.h"
22#include "qgssettings.h"
23#include "qgssqliteutils.h"
24
25#include <QString>
26#include <QTextStream>
27#include <QUuid>
28
29#include "moc_qgsbookmarkmanager.cpp"
30
31using namespace Qt::StringLiterals;
32
33//
34// QgsBookMark
35//
36
37QString QgsBookmark::id() const
38{
39 return mId;
40}
41
42void QgsBookmark::setId( const QString &id )
43{
44 mId = id;
45}
46
47QgsBookmark QgsBookmark::fromXml( const QDomElement &element, const QDomDocument & )
48{
50 b.setId( element.attribute( u"id"_s ) );
51 b.setName( element.attribute( u"name"_s ) );
52 b.setGroup( element.attribute( u"group"_s ) );
53 const QgsRectangle e = QgsRectangle::fromWkt( element.attribute( u"extent"_s ) );
54 b.setRotation( element.attribute( u"rotation"_s ).toDouble() );
56 crs.readXml( element );
57 b.setExtent( QgsReferencedRectangle( e, crs ) );
58 return b;
59}
60
61QDomElement QgsBookmark::writeXml( QDomDocument &doc ) const
62{
63 QDomElement bookmarkElem = doc.createElement( u"Bookmark"_s );
64 bookmarkElem.setAttribute( u"id"_s, mId );
65 bookmarkElem.setAttribute( u"name"_s, mName );
66 bookmarkElem.setAttribute( u"group"_s, mGroup );
67 bookmarkElem.setAttribute( u"extent"_s, mExtent.asWktPolygon() );
68 bookmarkElem.setAttribute( u"rotation"_s, mRotation );
69 mExtent.crs().writeXml( bookmarkElem, doc );
70 return bookmarkElem;
71}
72
73bool QgsBookmark::operator==( const QgsBookmark &other ) const
74{
75 return mId == other.mId
76 && mName == other.mName
77 && mExtent == other.mExtent
78 && mGroup == other.mGroup
79 && qgsDoubleNear( mRotation, other.mRotation );
80}
81
82bool QgsBookmark::operator!=( const QgsBookmark &other ) const
83{
84 return !( *this == other );
85}
86
87QString QgsBookmark::name() const
88{
89 return mName;
90}
91
92void QgsBookmark::setName( const QString &name )
93{
94 mName = name;
95}
96
97QString QgsBookmark::group() const
98{
99 return mGroup;
100}
101
102void QgsBookmark::setGroup( const QString &group )
103{
104 mGroup = group;
105}
106
108{
109 return mExtent;
110}
111
113{
114 mExtent = extent;
115}
116
118{
119 return mRotation;
120}
121
123{
124 mRotation = rotation;
125}
126
127
128//
129// QgsBookmarkManager
130//
131
133{
134 QgsBookmarkManager *res = new QgsBookmarkManager( project );
135 res->mProject = project;
136 return res;
137}
138
140 : QObject( parent )
141{
142 // we defer actually loading bookmarks until initialize() is called..
143}
144
149
150QString QgsBookmarkManager::addBookmark( const QgsBookmark &b, bool *ok )
151{
152 if ( ok )
153 *ok = false;
154
155 QgsBookmark bookmark = b;
156 if ( bookmark.id().isEmpty() )
157 bookmark.setId( QUuid::createUuid().toString() );
158 else
159 {
160 // check for duplicate ID
161 for ( const QgsBookmark &b : std::as_const( mBookmarks ) )
162 {
163 if ( b.id() == bookmark.id() )
164 {
165 return QString();
166 }
167 }
168 }
169
170 if ( ok )
171 *ok = true;
172
173 emit bookmarkAboutToBeAdded( bookmark.id() );
174 mBookmarks << bookmark;
175 if ( !mGroups.contains( bookmark.group() ) )
176 mGroups << bookmark.group();
177 emit bookmarkAdded( bookmark.id() );
178 if ( mProject )
179 {
180 mProject->setDirty( true );
181 }
182
183 return bookmark.id();
184}
185
186bool QgsBookmarkManager::removeBookmark( const QString &id )
187{
188 if ( id.isEmpty() )
189 return false;
190
191 QString group;
192 int pos = -1;
193 int i = 0;
194 for ( const QgsBookmark &b : std::as_const( mBookmarks ) )
195 {
196 if ( b.id() == id )
197 {
198 group = b.group();
199 pos = i;
200 break;
201 }
202 i++;
203 }
204
205 if ( pos < 0 )
206 return false;
207
208 emit bookmarkAboutToBeRemoved( id );
209 mBookmarks.removeAt( pos );
210 if ( bookmarksByGroup( group ).isEmpty() )
211 mGroups.removeOne( group );
212 emit bookmarkRemoved( id );
213 if ( mProject )
214 {
215 mProject->setDirty( true );
216 }
217
218 return true;
219}
220
222{
223 // check for duplicate ID
224 int i = 0;
225 for ( const QgsBookmark &b : std::as_const( mBookmarks ) )
226 {
227 if ( b.id() == bookmark.id() )
228 {
229 if ( mBookmarks[i].group() != bookmark.group() )
230 {
231 if ( bookmarksByGroup( mBookmarks[i].group() ).count() == 1 )
232 mGroups.removeOne( mBookmarks[i].group() );
233 if ( !mGroups.contains( bookmark.group() ) )
234 mGroups << bookmark.group();
235 }
236 mBookmarks[i] = bookmark;
237 emit bookmarkChanged( bookmark.id() );
238 if ( mProject )
239 {
240 mProject->setDirty( true );
241 }
242
243 return true;
244 }
245 i++;
246 }
247 return false;
248}
249
251{
252 const QList< QgsBookmark > bookmarks = mBookmarks;
253 for ( const QgsBookmark &b : bookmarks )
254 {
255 removeBookmark( b.id() );
256 }
257}
258
259QStringList QgsBookmarkManager::groups() const
260{
261 return mGroups;
262}
263
264void QgsBookmarkManager::renameGroup( const QString &oldName, const QString &newName )
265{
266 for ( int i = 0; i < mBookmarks.count(); ++i )
267 {
268 if ( mBookmarks.at( i ).group() == oldName )
269 {
270 mBookmarks[ i ].setGroup( newName );
271 emit bookmarkChanged( mBookmarks.at( i ).id() );
272 }
273 }
274}
275
276QList<QgsBookmark> QgsBookmarkManager::bookmarks() const
277{
278 return mBookmarks;
279}
280
282{
283 for ( const QgsBookmark &b : mBookmarks )
284 {
285 if ( b.id() == id )
286 return b;
287 }
288 return QgsBookmark();
289}
290
291QList<QgsBookmark> QgsBookmarkManager::bookmarksByGroup( const QString &group )
292{
293 QList<QgsBookmark> bookmarks;
294 for ( const QgsBookmark &b : mBookmarks )
295 {
296 if ( b.group() == group )
297 bookmarks << b;
298 }
299 return bookmarks;
300}
301
302bool QgsBookmarkManager::readXml( const QDomElement &element, const QDomDocument &doc )
303{
304 clear();
305
306 QDomElement bookmarksElem = element;
307 if ( element.tagName() != "Bookmarks"_L1 )
308 {
309 bookmarksElem = element.firstChildElement( u"Bookmarks"_s );
310 }
311 bool result = true;
312 if ( mProject && bookmarksElem.isNull() )
313 {
314 // handle legacy projects
315 const int count = mProject->readNumEntry( u"Bookmarks"_s, u"/count"_s );
316 for ( int i = 0; i < count; ++i )
317 {
318 const double minX = mProject->readDoubleEntry( u"Bookmarks"_s, u"/Row-%1/MinX"_s.arg( i ) );
319 const double minY = mProject->readDoubleEntry( u"Bookmarks"_s, u"/Row-%1/MinY"_s.arg( i ) );
320 const double maxX = mProject->readDoubleEntry( u"Bookmarks"_s, u"/Row-%1/MaxX"_s.arg( i ) );
321 const double maxY = mProject->readDoubleEntry( u"Bookmarks"_s, u"/Row-%1/MaxY"_s.arg( i ) );
322 const long srid = mProject->readNumEntry( u"Bookmarks"_s, u"/Row-%1/SRID"_s.arg( i ) );
323 QgsBookmark b;
324 b.setId( u"bookmark_%1"_s.arg( i ) );
325 b.setName( mProject->readEntry( u"Bookmarks"_s, u"/Row-%1/Name"_s.arg( i ) ) );
327
328 bool added = false;
329 addBookmark( b, &added );
330 result = added && result;
331 }
332 return result;
333 }
334
335 //restore each
336 QDomNodeList bookmarkNodes = element.elementsByTagName( u"Bookmark"_s );
337 for ( int i = 0; i < bookmarkNodes.size(); ++i )
338 {
339 QgsBookmark b = QgsBookmark::fromXml( bookmarkNodes.at( i ).toElement(), doc );
340 bool added = false;
341 addBookmark( b, &added );
342 result = added && result;
343 }
344
345 return result;
346}
347
348QDomElement QgsBookmarkManager::writeXml( QDomDocument &doc ) const
349{
350 QDomElement bookmarksElem = doc.createElement( u"Bookmarks"_s );
351
352 for ( const QgsBookmark &b : mBookmarks )
353 {
354 QDomElement bookmarkElem = b.writeXml( doc );
355 bookmarksElem.appendChild( bookmarkElem );
356 }
357 return bookmarksElem;
358}
359
360bool QgsBookmarkManager::moveBookmark( const QString &id, QgsBookmarkManager *destination )
361{
362 QgsBookmark b = bookmarkById( id );
363 if ( b.id().isEmpty() )
364 return false;
365
366 removeBookmark( id );
367 bool ok = false;
368 destination->addBookmark( b, &ok );
369 return ok;
370}
371
372bool QgsBookmarkManager::exportToFile( const QString &path, const QList<const QgsBookmarkManager *> &managers, const QString &group )
373{
374 // note - we don't use the other writeXml implementation, to maintain older format compatibility
375 QDomDocument doc( u"qgis_bookmarks"_s );
376 QDomElement root = doc.createElement( u"qgis_bookmarks"_s );
377 doc.appendChild( root );
378
379 QList<QString> headerList;
380 headerList
381 << u"project"_s
382 << u"xmin"_s
383 << u"ymin"_s
384 << u"xmax"_s
385 << u"ymax"_s
386 << u"rotation"_s
387 << u"sr_id"_s;
388
389 for ( const QgsBookmarkManager *manager : managers )
390 {
391 const QList< QgsBookmark > bookmarks = manager->bookmarks();
392 for ( const QgsBookmark &b : bookmarks )
393 {
394 if ( !group.isEmpty() && b.group() != group )
395 continue;
396
397 QDomElement bookmark = doc.createElement( u"bookmark"_s );
398 root.appendChild( bookmark );
399
400 QDomElement id = doc.createElement( u"id"_s );
401 id.appendChild( doc.createTextNode( b.id() ) );
402 bookmark.appendChild( id );
403
404 QDomElement name = doc.createElement( u"name"_s );
405 name.appendChild( doc.createTextNode( b.name() ) );
406 bookmark.appendChild( name );
407
408 QDomElement group = doc.createElement( u"project"_s );
409 group.appendChild( doc.createTextNode( b.group() ) );
410 bookmark.appendChild( group );
411
412 QDomElement xMin = doc.createElement( u"xmin"_s );
413 xMin.appendChild( doc.createTextNode( qgsDoubleToString( b.extent().xMinimum() ) ) );
414 bookmark.appendChild( xMin );
415 QDomElement yMin = doc.createElement( u"ymin"_s );
416 yMin.appendChild( doc.createTextNode( qgsDoubleToString( b.extent().yMinimum() ) ) );
417 bookmark.appendChild( yMin );
418 QDomElement xMax = doc.createElement( u"xmax"_s );
419 xMax.appendChild( doc.createTextNode( qgsDoubleToString( b.extent().xMaximum() ) ) );
420 bookmark.appendChild( xMax );
421 QDomElement yMax = doc.createElement( u"ymax"_s );
422 yMax.appendChild( doc.createTextNode( qgsDoubleToString( b.extent().yMaximum() ) ) );
423 bookmark.appendChild( yMax );
424
425 QDomElement rotation = doc.createElement( u"rotation"_s );
426 rotation.appendChild( doc.createTextNode( qgsDoubleToString( b.rotation() ) ) );
427 bookmark.appendChild( rotation );
428
429 QDomElement crs = doc.createElement( u"sr_id"_s );
430 crs.appendChild( doc.createTextNode( QString::number( b.extent().crs().srsid() ) ) );
431 bookmark.appendChild( crs );
432 }
433 }
434
435 QFile f( path );
436 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
437 {
438 f.close();
439 return false;
440 }
441
442 QTextStream out( &f );
443 doc.save( out, 2 );
444 f.close();
445
446 return true;
447}
448
449bool QgsBookmarkManager::importFromFile( const QString &path )
450{
451 if ( path.isEmpty() )
452 {
453 return false;
454 }
455
456 QFile f( path );
457 if ( !f.open( QIODevice::ReadOnly | QIODevice::Text ) )
458 {
459 return false;
460 }
461
462 QDomDocument doc;
463 if ( !doc.setContent( &f ) )
464 {
465 return false;
466 }
467 f.close();
468
469 QDomElement docElem = doc.documentElement();
470 QDomNodeList nodeList = docElem.elementsByTagName( u"bookmark"_s );
471
472 bool res = true;
473 for ( int i = 0; i < nodeList.count(); i++ )
474 {
475 QDomNode bookmark = nodeList.at( i );
476 QDomElement name = bookmark.firstChildElement( u"name"_s );
477 QDomElement prjname = bookmark.firstChildElement( u"project"_s );
478 QDomElement xmin = bookmark.firstChildElement( u"xmin"_s );
479 QDomElement ymin = bookmark.firstChildElement( u"ymin"_s );
480 QDomElement xmax = bookmark.firstChildElement( u"xmax"_s );
481 QDomElement ymax = bookmark.firstChildElement( u"ymax"_s );
482 QDomElement rotation = bookmark.firstChildElement( u"rotation"_s );
483 QDomElement srid = bookmark.firstChildElement( u"sr_id"_s );
484
485 bool ok = false;
486 QgsBookmark b;
487 b.setName( name.text() );
488 b.setGroup( prjname.text() );
490 crs.createFromSrsId( srid.text().toLongLong() );
491 b.setExtent( QgsReferencedRectangle( QgsRectangle( xmin.text().toDouble(),
492 ymin.text().toDouble(),
493 xmax.text().toDouble(),
494 ymax.text().toDouble() ), crs ) );
495 b.setRotation( rotation.text().toDouble() );
496 addBookmark( b, &ok );
497 res = res && ok;
498 }
499
500 return res;
501}
502
503void QgsBookmarkManager::store()
504{
505 if ( !mFilePath.isEmpty() )
506 {
507 QFile f( mFilePath );
508 if ( !f.open( QFile::WriteOnly | QIODevice::Truncate ) )
509 {
510 f.close();
511 return;
512 }
513
514 QDomDocument doc;
515 QDomElement elem = writeXml( doc );
516 doc.appendChild( elem );
517
518 QTextStream out( &f );
519 doc.save( out, 2 );
520 f.close();
521 }
522}
523
524void QgsBookmarkManager::initialize( const QString &filePath )
525{
526 if ( mInitialized )
527 return;
528
529 mFilePath = filePath;
530
531 mInitialized = true;
532
533 // restore state
534 if ( !QFileInfo::exists( mFilePath ) )
535 {
536 //convert old bookmarks from db
538 int result = database.open( QgsApplication::qgisUserDatabaseFilePath() );
539 if ( result != SQLITE_OK )
540 {
541 return;
542 }
543
544 sqlite3_statement_unique_ptr preparedStatement = database.prepare( u"SELECT name,xmin,ymin,xmax,ymax,projection_srid FROM tbl_bookmarks"_s, result );
545 if ( result == SQLITE_OK )
546 {
547 while ( preparedStatement.step() == SQLITE_ROW )
548 {
549 const QString name = preparedStatement.columnAsText( 0 );
550 const double xMin = preparedStatement.columnAsDouble( 1 );
551 const double yMin = preparedStatement.columnAsDouble( 2 );
552 const double xMax = preparedStatement.columnAsDouble( 3 );
553 const double yMax = preparedStatement.columnAsDouble( 4 );
554 const long long srid = preparedStatement.columnAsInt64( 5 );
555
556 QgsBookmark b;
557 b.setName( name );
558 const QgsRectangle extent( xMin, yMin, xMax, yMax );
560 addBookmark( b );
561 }
562 }
563 store();
564 }
565 else
566 {
567 QFile f( mFilePath );
568 if ( !f.open( QIODevice::ReadOnly | QIODevice::Text ) )
569 {
570 return;
571 }
572
573 QDomDocument doc;
574 if ( !doc.setContent( &f ) )
575 {
576 return;
577 }
578 f.close();
579
580 QDomElement elem = doc.documentElement();
581 readXml( elem, doc );
582 }
583}
static QString qgisUserDatabaseFilePath()
Returns the path to the user qgis.db file.
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
void setRotation(double rotation)
Sets the bookmark's spatial map rotation.
QgsBookmark()=default
Default constructor, creates an empty bookmark.
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.
double rotation() const
Returns the bookmark's map rotation.
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.
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.
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:112
A rectangle specified with double values.
static QgsRectangle fromWkt(const QString &wkt)
Creates a new rectangle from a wkt string.
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:6817
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900