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