Access and create bookmarks on a map.

Use case
Bookmarks are used for easily storing and accessing saved locations on the map. Bookmarks are of interest in educational apps (e.g. touring historical sites) or more specifically, for a land management company wishing to visually monitor flood levels over time at a particular location. These locations can be saved as bookmarks and revisited easily each time their basemap data has been updated (e.g. working with up to date satellite imagery to monitor water levels).
How to use the sample
The map in the sample comes pre-populated with a set of bookmarks. To access a bookmark and move to that location, click on a bookmark’s name from the list. To add a bookmark, pan and/or zoom to a new location and click on the ’+’ in the bottom corner. Enter a unique name for the bookmark and click ok, and the bookmark will be added to the list
How it works
- Create a new
Mapobject and create aBookmarkListwithMap::bookmarks(). - To create a new bookmark and add it to the bookmark list:
- Create a new
Bookmarkobject passing in text (the name of the bookmark) and aViewpointas parameters. - Add the new bookmark to the book mark list with
BookmarkListModel::append(bookmark).
- Create a new
Relevant API
- Bookmark
- BookmarkListModel
- Viewpoint
Tags
bookmark, extent, location, zoom
Sample Code
// [WriteFile Name=ManageBookmarks, Category=Maps]// [Legal]// Copyright 2016 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifdef PCH_BUILD#include "pch.hpp"#endif // PCH_BUILD
// sample headers#include "ManageBookmarks.h"
// ArcGIS Maps SDK headers#include "Basemap.h"#include "Bookmark.h"#include "BookmarkListModel.h"#include "Envelope.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "SpatialReference.h"#include "Viewpoint.h"
// Qt headers#include <QFuture>
using namespace Esri::ArcGISRuntime;
ManageBookmarks::ManageBookmarks(QQuickItem* parent) : QQuickItem(parent){}
ManageBookmarks::~ManageBookmarks() = default;
void ManageBookmarks::init(){ qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<ManageBookmarks>("Esri.Samples", 1, 0, "ManageBookmarksSample"); qmlRegisterUncreatableType<QAbstractListModel>("Esri.Samples", 1, 0, "AbstractListModel", "AbstractListModel is uncreateable");}
void ManageBookmarks::componentComplete(){ QQuickItem::componentComplete();
// find QML MapView component m_mapView = findChild<MapQuickView*>("mapView");
// create a new basemap instance Basemap* basemap = new Basemap(BasemapStyle::ArcGISImagery, this); // create a new map instance m_map = new Map(basemap, this); // set map on the map view m_mapView->setMap(m_map);
// create the bookmarks once the map is loaded connect(m_map, &Esri::ArcGISRuntime::Map::loadStatusChanged, this, [this](Esri::ArcGISRuntime::LoadStatus loadStatus) { if (loadStatus == LoadStatus::Loaded) { m_bookmarks = m_map->bookmarks(); createInitialBookmarks(); } });}
void ManageBookmarks::createInitialBookmarks(){ // Mysterious Desert Pattern Envelope env1(3742993.127298778, 3170396.4675719286, 3744795.1333054285, 3171745.88077, SpatialReference(102100)); Viewpoint viewpoint1(env1); createBookmark("Mysterious Desert Pattern", viewpoint1);
// Strange Symbol Envelope env2(-13009913.860076642, 4495026.9307899885, -13009442.089218518, 4495404.031910696, SpatialReference(102100)); Viewpoint viewpoint2(env2); createBookmark("Strange Symbol", viewpoint2);
// Guitar-Shaped Forest Envelope env3(-7124497.45137465, -4012221.6124684606, -7121131.417429369, -4009697.0870095, SpatialReference(102100)); Viewpoint viewpoint3(env3); createBookmark("Guitar-Shaped Forest", viewpoint3);
// Grand Prismatic Spring Envelope env4(-12338668.348591767, 5546908.424239618, -12338247.594362013, 5547223.989911933, SpatialReference(102100)); Viewpoint viewpoint4(env4); createBookmark("Grand Prismatic Spring", viewpoint4);}
void ManageBookmarks::createBookmark(QString name, Viewpoint viewpoint){ //! [Add bookmarks to the list model] // Get the bookmark list BookmarkListModel* bookmarks = dynamic_cast<BookmarkListModel*>(m_bookmarks); if (!bookmarks) return;
// Create the bookmark from the name and viewpoint Bookmark* bookmark = new Bookmark(name, viewpoint, this);
// Add it to the map's bookmark list bookmarks->append(bookmark);
// emit that model has changed emit bookmarksChanged(); //! [Add bookmarks to the list model]}
void ManageBookmarks::goToBookmark(int bookmarkIndex){ BookmarkListModel* bookmarks = dynamic_cast<BookmarkListModel*>(m_bookmarks); if (!bookmarks) return;
m_mapView->setViewpointAsync(bookmarks->at(bookmarkIndex)->viewpoint());}
QAbstractListModel* ManageBookmarks::bookmarks() const{ return m_bookmarks;}
void ManageBookmarks::addBookmark(QString newBookmarkName){ createBookmark(newBookmarkName, m_mapView->currentViewpoint(ViewpointType::BoundingGeometry));}
QString ManageBookmarks::bookmarkNameForIndex(int index) const{ return m_bookmarks->data(m_bookmarks->index(index), BookmarkListModel::BookmarkNameRole).toString();}// [WriteFile Name=ManageBookmarks, Category=Maps]// [Legal]// Copyright 2016 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
#ifndef MANAGE_BOOKMARKS_H#define MANAGE_BOOKMARKS_H
// Qt headers#include <QAbstractListModel>#include <QQuickItem>
namespace Esri::ArcGISRuntime{ class Map; class MapQuickView; class Viewpoint;}
class QString;
class ManageBookmarks : public QQuickItem{ Q_OBJECT //! [Expose the list model to QML] Q_PROPERTY(QAbstractListModel* bookmarks READ bookmarks NOTIFY bookmarksChanged) //! [Expose the list model to QML]
public: explicit ManageBookmarks(QQuickItem* parent = nullptr); ~ManageBookmarks() override;
void componentComplete() override; static void init(); Q_INVOKABLE void goToBookmark(int bookmarkIndex); Q_INVOKABLE void addBookmark(QString newBookmarkName);
Q_INVOKABLE QString bookmarkNameForIndex(int index) const;
signals: void bookmarksChanged();
private: void createInitialBookmarks(); void createBookmark(QString name, Esri::ArcGISRuntime::Viewpoint viewpoint); QAbstractListModel* bookmarks() const;
private: Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; QAbstractListModel* m_bookmarks = nullptr;};
#endif // MANAGE_BOOKMARKS_H// [WriteFile Name=ManageBookmarks, Category=Maps]// [Legal]// Copyright 2016 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
import QtQuickimport QtQuick.Controlsimport QtQuick.Layoutsimport Esri.Samples
ManageBookmarksSample { id: manageBookmarksSample width: 800 height: 600
// add a mapView component MapView { id: mapView anchors.fill: parent objectName: "mapView"
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); }
// Create the add button so new bookmarks can be added Rectangle { id: addButton property bool pressed: false anchors { right: parent.right bottom: mapView.attributionTop rightMargin: 20 bottomMargin: 20 } width: childrenRect.width height: childrenRect.height color: pressed ? "#959595" : "#D6D6D6" radius: 100 border { color: "#585858" width: 1 }
Image { rotation: 45 source: "qrc:/Samples/Maps/ManageBookmarks/add.png" width: 32 height: 32 }
MouseArea { anchors.fill: parent onPressed: addButton.pressed = true onReleased: addButton.pressed = false onClicked: { // Show the add window when it is clicked addWindow.visible = true; } } } }
//! [Use BookmarkListModel in View] ComboBox { id: bookmarkComboBox anchors { left: parent.left top: parent.top margins: 15 } property int bestWidth: implicitWidth width: bestWidth + leftPadding + rightPadding + (indicator ? indicator.width : 10) // Set the model to the BookmarkListModel model: manageBookmarksSample.bookmarks
onModelChanged: { const model = bookmarkComboBox.model; if (model) { let w = bestWidth; for (let i = 0; i < model.rowCount(); ++i) { metrics.text = manageBookmarksSample.bookmarkNameForIndex(i); w = Math.max(w, metrics.width); } bestWidth = w; } }
onCurrentTextChanged: { // Call C++ invokable function to to go to the bookmark manageBookmarksSample.goToBookmark(bookmarkComboBox.currentIndex); }
// Add a background to the ComboBox Rectangle { anchors.fill: parent radius: 10 // Make the rectangle visible if a dropdown indicator exists // An indicator only exists if a theme is set visible: parent.indicator border.width: 1 }
TextMetrics { id: metrics font: bookmarkComboBox.font } } //! [Use BookmarkListModel in View]
// Create a window so names for new bookmarks can be specified Rectangle { id: addWindow visible: false enabled: visible color: "lightgrey" opacity: .9 radius: 5 anchors.centerIn: parent width: childrenRect.width height: childrenRect.height border { color: "#4D4D4D" width: 1 }
MouseArea { anchors.fill: parent onClicked: mouse => mouse.accepted = true; }
GridLayout { columns: 2 Text { text: qsTr("Provide the bookmark name") Layout.columnSpan: 2 Layout.margins: 5 }
TextField { id: textField placeholderText: qsTr("ex: Grand Canyon") selectByMouse: true Layout.columnSpan: 2 Layout.margins: 5 Layout.fillWidth: true }
Button { text: qsTr("Cancel") onClicked: addWindow.visible = false; Layout.margins: 5 }
Button { text: qsTr("Done") Layout.margins: 5 Layout.alignment: Qt.AlignRight onClicked: { // Call the C++ invokable function and pass in the name specified manageBookmarksSample.addBookmark(textField.text); bookmarkComboBox.currentIndex = bookmarkComboBox.count - 1; textField.text = ""; addWindow.visible = false; } } } }}// [Legal]// Copyright 2015 Esri.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// [Legal]
// sample headers#include "ManageBookmarks.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QCommandLineParser>#include <QDir>#include <QGuiApplication>#include <QQmlEngine>#include <QQuickView>#include <QSettings>
// Platform specific headers#ifdef Q_OS_WIN#include <Windows.h>#endif
#define STRINGIZE(x) #x#define QUOTE(x) STRINGIZE(x)
int main(int argc, char *argv[]){ Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setUseLegacyAuthentication(false); QGuiApplication app(argc, argv); app.setApplicationName(QString("ManageBookmarks"));
// Use of ArcGIS location services, such as basemap styles, geocoding, and routing services, // requires an access token. For more information see // https://links.esri.com/arcgis-runtime-security-auth.
// The following methods grant an access token:
// 1. User authentication: Grants a temporary access token associated with a user's ArcGIS account. // To generate a token, a user logs in to the app with an ArcGIS account that is part of an // organization in ArcGIS Online or ArcGIS Enterprise.
// 2. API key authentication: Get a long-lived access token that gives your application access to // ArcGIS location services. Go to the tutorial at https://links.esri.com/create-an-api-key. // Copy the API Key access token.
const QString accessToken = QString("");
if (accessToken.isEmpty()) { qWarning() << "Use of ArcGIS location services, such as the basemap styles service, requires" << "you to authenticate with an ArcGIS account or set the API Key property."; } else { Esri::ArcGISRuntime::ArcGISRuntimeEnvironment::setApiKey(accessToken); }
// Initialize the sample ManageBookmarks::init();
/* Leaving in for Doc snippet //! [Register the list model for QML] qmlRegisterUncreatableType<BookmarkListModel>("Esri.Samples", 1, 0, "BookmarkListModel", "BookmarkListModel is an uncreatable type"); //! [Register the list model for QML] */
// Initialize application view QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView);
// Add the import Path view.engine()->addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml"));
QString arcGISRuntimeImportPath = QUOTE(ARCGIS_RUNTIME_IMPORT_PATH);
#if defined(LINUX_PLATFORM_REPLACEMENT) // on some linux platforms the string 'linux' is replaced with 1 // fix the replacement paths which were created QString replaceString = QUOTE(LINUX_PLATFORM_REPLACEMENT); arcGISRuntimeImportPath = arcGISRuntimeImportPath.replace(replaceString, "linux", Qt::CaseSensitive); #endif
// Add the Runtime and Extras path view.engine()->addImportPath(arcGISRuntimeImportPath);
// Set the source view.setSource(QUrl("qrc:/Samples/Maps/ManageBookmarks/ManageBookmarks.qml"));
view.show();
return app.exec();}