List features related to a selected feature.

Use case
Related features are useful for managing relational information, like what you would store in a relational database management system (RDBMS). You can define relationship between records as one-to-one, one-to-many, or many-to-one. For example, you could model inspections and facilities as a many-to-one relationship. Then, for any facility feature, you could list related inspection features.
How to use the sample
Click on a feature to select it. The related features will be displayed in a list.
How it works
- With a
Feature, callqueryRelatedFeaturesAsyncon the feature’s feature table. - Iterate over the result’s collection of
RelatedFeatureQueryResultobjects to get the related features and add them to a list.
Relevant API
- ArcGISFeature
- ArcGISFeatureTable
- FeatureQueryResult
- ArcGISFeatureTable::queryRelatedFeaturesAsync
- RelatedFeatureQueryResult
Tags
features, identify, query, related, relationship, search
Sample Code
// [WriteFile Name=ListRelatedFeatures, Category=Features]// [Legal]// Copyright 2017 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 "ListRelatedFeatures.h"#include "RelatedFeature.h"#include "RelatedFeatureListModel.h"
// ArcGIS Maps SDK headers#include "ArcGISFeature.h"#include "ArcGISFeatureLayerInfo.h"#include "ArcGISFeatureTable.h"#include "AttributeListModel.h"#include "CoreTypes.h"#include "Envelope.h"#include "Error.h"#include "FeatureIterator.h"#include "FeatureLayer.h"#include "FeatureQueryResult.h"#include "LayerListModel.h"#include "Map.h"#include "MapQuickView.h"#include "MapTypes.h"#include "Point.h"#include "QueryParameters.h"#include "RelatedFeatureQueryResult.h"#include "SelectionProperties.h"#include "SpatialReference.h"#include "ViewInsets.h"
// Qt headers#include <QFuture>#include <QList>#include <QUrl>#include <QUuid>
// STL headers#include <memory>
using namespace Esri::ArcGISRuntime;
namespace{ // Convenience RAII struct that deletes all pointers in given container. struct FeatureQueryListResultLock { FeatureQueryListResultLock(const QList<RelatedFeatureQueryResult*>& list) : results(list) { } ~FeatureQueryListResultLock() { qDeleteAll(results); } const QList<RelatedFeatureQueryResult*>& results; };}
ListRelatedFeatures::ListRelatedFeatures(QQuickItem* parent /* = nullptr */): QQuickItem(parent){}
void ListRelatedFeatures::init(){ // Register the map view for QML qmlRegisterType<MapQuickView>("Esri.Samples", 1, 0, "MapView"); qmlRegisterType<ListRelatedFeatures>("Esri.Samples", 1, 0, "ListRelatedFeaturesSample"); qmlRegisterAnonymousType<ViewInsets>("Esri.Samples", 1); qmlRegisterUncreatableType<RelatedFeatureListModel>("Esri.ArcGISRuntimeSamples", 1, 0, "RelatedFeatureListModel", "RelatedFeatureListModel is an uncreatable type");}
void ListRelatedFeatures::componentComplete(){ QQuickItem::componentComplete();
// Create a list model to store the related features m_relatedFeaturesModel = new RelatedFeatureListModel(this); emit relatedFeaturesModelChanged();
// find QML MapView component m_mapView = findChild<MapQuickView*>("mapView"); m_mapView->setSelectionProperties(SelectionProperties(QColor(Qt::yellow)));
// Create a map using the URL of a web map m_map = new Map(QUrl("https://arcgis.com/home/item.html?id=dcc7466a91294c0ab8f7a094430ab437"), this);
// Set map to map view m_mapView->setMap(m_map);
// connect signals connectSignals();}
void ListRelatedFeatures::connectSignals(){ // connect to doneLoading signal connect(m_map, &Map::doneLoading, this, [this](const Error& loadError) { if (!loadError.isEmpty()) return;
bool foundLayer = false; // Found the Alaska National Parks layer. for (int i = 0; i < m_map->operationalLayers()->size() || !foundLayer; ++i) { // get the Alaska National Parks layer if (m_map->operationalLayers()->at(i)->name().contains(QStringLiteral("- Alaska National Parks"))) { foundLayer = true; m_alaskaNationalParks = static_cast<FeatureLayer*>(m_map->operationalLayers()->at(i)); m_alaskaFeatureTable = static_cast<ArcGISFeatureTable*>(m_alaskaNationalParks->featureTable()); } } });
// connect to the mouseClicked signal connect(m_mapView, &MapQuickView::mouseClicked, this, [this](QMouseEvent& mouseEvent) { // hide the attribute view emit hideAttributeTable();
// clear the list model m_relatedFeaturesModel->clear();
// create objects required to do a selection with a query Point clickPoint = m_mapView->screenToLocation(mouseEvent.position().x(), mouseEvent.position().y()); double mapTolerance = 10 * m_mapView->unitsPerDIP(); Envelope envelope = Envelope(clickPoint.x() - mapTolerance, clickPoint.y() - mapTolerance, clickPoint.x() + mapTolerance, clickPoint.y() + mapTolerance, m_map->spatialReference()); QueryParameters queryParams; queryParams.setGeometry(envelope); queryParams.setSpatialRelationship(SpatialRelationship::Intersects);
// clear any selections m_alaskaNationalParks->clearSelection();
// select features m_alaskaNationalParks->selectFeaturesAsync(queryParams, SelectionMode::New).then(this, [this](FeatureQueryResult* rawResult) { auto result = std::unique_ptr<FeatureQueryResult>(rawResult); // The result could contain more than 1 feature, but we assume that // there is only ever 1. If more are given they are ignored. We // are only interested in the first (and only) feature. if (result->iterator().hasNext()) { m_selectedFeature = static_cast<ArcGISFeature*>(result->iterator().next(this));
// zoom to the selected feature m_mapView->setViewpointGeometryAsync(m_selectedFeature->geometry().extent(), 100);
// query related features m_alaskaFeatureTable->queryRelatedFeaturesAsync(m_selectedFeature).then(this, [this](QList<RelatedFeatureQueryResult*> rawRelatedResults) { FeatureQueryListResultLock lock(rawRelatedResults); for (const RelatedFeatureQueryResult* relatedResult : lock.results) { while (relatedResult->iterator().hasNext()) { // get the related feature const ArcGISFeature* feature = static_cast<ArcGISFeature*>(relatedResult->iterator().next()); const ArcGISFeatureTable* relatedTable = static_cast<ArcGISFeatureTable*>(feature->featureTable()); const QString displayFieldName = relatedTable->layerInfo().displayFieldName(); const QString serviceLayerName = relatedTable->layerInfo().serviceLayerName(); const QString displayFieldValue = feature->attributes()->attributeValue(displayFieldName).toString();
// add the related feature to the list model RelatedFeature relatedFeature = RelatedFeature(displayFieldName, displayFieldValue, serviceLayerName); m_relatedFeaturesModel->addRelatedFeature(relatedFeature); emit relatedFeaturesModelChanged(); } }
if (m_selectedFeature) { delete m_selectedFeature; m_selectedFeature = nullptr; }
emit showAttributeTable(); }); } }); });}
QAbstractListModel* ListRelatedFeatures::relatedFeaturesModel() const{ return m_relatedFeaturesModel;}// [WriteFile Name=ListRelatedFeatures, Category=Features]// [Legal]// Copyright 2017 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 LISTRELATEDFEATURES_H#define LISTRELATEDFEATURES_H
// Qt headers#include <QQuickItem>
namespace Esri::ArcGISRuntime{ class ArcGISFeature; class ArcGISFeatureTable; class FeatureLayer; class Map; class MapQuickView;}class RelatedFeatureListModel;class QAbstractListModel;
Q_MOC_INCLUDE("QAbstractListModel")
class ListRelatedFeatures : public QQuickItem{ Q_OBJECT
Q_PROPERTY(QAbstractListModel* relatedFeaturesModel READ relatedFeaturesModel NOTIFY relatedFeaturesModelChanged)
public: explicit ListRelatedFeatures(QQuickItem* parent = nullptr); ~ListRelatedFeatures() override = default;
void componentComplete() override; static void init();
signals: void showAttributeTable(); void hideAttributeTable(); void relatedFeaturesModelChanged();
private: void connectSignals(); QAbstractListModel* relatedFeaturesModel() const;
Esri::ArcGISRuntime::Map* m_map = nullptr; Esri::ArcGISRuntime::MapQuickView* m_mapView = nullptr; Esri::ArcGISRuntime::FeatureLayer* m_alaskaNationalParks = nullptr; Esri::ArcGISRuntime::ArcGISFeatureTable* m_alaskaFeatureTable = nullptr; Esri::ArcGISRuntime::ArcGISFeature* m_selectedFeature = nullptr; RelatedFeatureListModel* m_relatedFeaturesModel = nullptr;};
#endif // LISTRELATEDFEATURES_H// [WriteFile Name=ListRelatedFeatures, Category=Features]// [Legal]// Copyright 2017 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 Esri.Samples
ListRelatedFeaturesSample { id: rootRectangle clip: true width: 800 height: 600
onHideAttributeTable: attributeView.height = 0; onShowAttributeTable: attributeView.height = 200;
MapView { id: mapView anchors.fill: parent objectName: "mapView"
Component.onCompleted: { // Set the focus on MapView to initially enable keyboard navigation forceActiveFocus(); }
// bind the insets to the attribute view so the attribution text shows when the view expands viewInsets.bottom: attributeView.height }
Rectangle { id: attributeView anchors { left: parent.left right: parent.right bottom: parent.bottom } height: 0
// Animate the expand and collapse of the legend Behavior on height { SpringAnimation { spring: 3 damping: 0.4 } }
ListView { anchors { fill: parent margins: 5 }
clip: true model: relatedFeaturesModel spacing: 5
// Create delegate to display the attributes delegate: Rectangle { width: rootRectangle.width height: childrenRect.height color: "transparent"
Text { anchors.horizontalCenter: parent.horizontalCenter anchors.margins: 10
text: displayFieldValue font.pixelSize: 12 } }
// Create a section to separate features by table section { property: "serviceLayerName" criteria: ViewSection.FullString labelPositioning: ViewSection.CurrentLabelAtStart | ViewSection.InlineLabels delegate: Rectangle { width: rootRectangle.width height: childrenRect.height color: "lightsteelblue"
Text { anchors.horizontalCenter: parent.horizontalCenter text: section font { bold: true pixelSize: 13 } } } } } }}// [WriteFile Name=ListRelatedFeatures, Category=Features]// [Legal]// Copyright 2017 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 "RelatedFeature.h"
// Qt headers#include <QString>
RelatedFeature::RelatedFeature(const QString& displayFieldName, const QString& displayFieldValue, const QString& serviceLayerName) : m_displayFieldName(displayFieldName), m_displayFieldValue(displayFieldValue), m_serviceLayerName(serviceLayerName){}// [WriteFile Name=ListRelatedFeatures, Category=Features]// [Legal]// Copyright 2017 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 RELATEDFEATURE_H#define RELATEDFEATURE_H
// Qt headers#include <QString>
class RelatedFeature{
public: explicit RelatedFeature(const QString& displayFieldName, const QString& displayFieldValue, const QString& serviceLayerName); ~RelatedFeature() = default;
public: QString displayFieldName() const { return m_displayFieldName; } QString displayFieldValue() const { return m_displayFieldValue; } QString serviceLayerName() const { return m_serviceLayerName; }
private: QString m_displayFieldName; QString m_displayFieldValue; QString m_serviceLayerName;};
#endif // RELATEDFEATURE_H// [WriteFile Name=ListRelatedFeatures, Category=Features]// [Legal]// Copyright 2017 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 "RelatedFeature.h"#include "RelatedFeatureListModel.h"
// Qt headers#include <QAbstractListModel>#include <QByteArray>#include <QHash>#include <QModelIndex>#include <QObject>#include <QVariant>
RelatedFeatureListModel::RelatedFeatureListModel(QObject* parent) : QAbstractListModel(parent){}
void RelatedFeatureListModel::addRelatedFeature(RelatedFeature relatedFeature){ beginInsertRows(QModelIndex(), rowCount(), rowCount()); m_relatedFeatures << relatedFeature; endInsertRows();}int RelatedFeatureListModel::rowCount(const QModelIndex& parent) const{ Q_UNUSED(parent); return m_relatedFeatures.count();}
QVariant RelatedFeatureListModel::data(const QModelIndex& index, int role) const{ if (index.row() < 0 || index.row() >= m_relatedFeatures.count()) return QVariant();
RelatedFeature relatedFeature = m_relatedFeatures[index.row()];
if (role == DisplayFieldNameRole) return relatedFeature.displayFieldName(); else if (role == DisplayFieldValueRole) return relatedFeature.displayFieldValue(); else if (role == ServiceLayerName) return relatedFeature.serviceLayerName(); return QVariant();}
QHash<int, QByteArray> RelatedFeatureListModel::roleNames() const{ QHash<int, QByteArray> roles; roles[DisplayFieldNameRole] = "displayFieldName"; roles[DisplayFieldValueRole] = "displayFieldValue"; roles[ServiceLayerName] = "serviceLayerName"; return roles;}
void RelatedFeatureListModel::clear(){ beginResetModel(); m_relatedFeatures.clear(); endResetModel();}// [WriteFile Name=ListRelatedFeatures, Category=Features]// [Legal]// Copyright 2017 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 RELATEDFEATURELISTMODEL_H#define RELATEDFEATURELISTMODEL_H
// sample headers#include "RelatedFeature.h"
// Qt headers#include <QAbstractListModel>#include <QByteArray>#include <QHash>#include <QList>
class RelatedFeatureListModel : public QAbstractListModel{ Q_OBJECT
public: enum RelatedRoles { DisplayFieldNameRole = Qt::UserRole + 1, DisplayFieldValueRole, ServiceLayerName };
explicit RelatedFeatureListModel(QObject* parent = nullptr); ~RelatedFeatureListModel() override = default;
void addRelatedFeature(RelatedFeature relatedFeature); int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; void clear(); int size() const { return m_relatedFeatures.size(); }
protected: QHash<int, QByteArray> roleNames() const override;
private: QList<RelatedFeature> m_relatedFeatures;};
#endif // RELATEDFEATURELISTMODEL_H// [Legal]// Copyright 2017 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 "ListRelatedFeatures.h"
// ArcGIS Maps SDK headers#include "ArcGISRuntimeEnvironment.h"
// Qt headers#include <QCommandLineParser>#include <QDir>#include <QGuiApplication>#include <QQmlEngine>#include <QQuickView>
// 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("ListRelatedFeatures"));
// 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. ListRelatedFeatures::init();
// Initialize application view QQuickView view; view.setResizeMode(QQuickView::SizeRootObjectToView);
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 import Path view.engine()->addImportPath(QDir(QCoreApplication::applicationDirPath()).filePath("qml")); // Add the Runtime and Extras path view.engine()->addImportPath(arcGISRuntimeImportPath);
// Set the source view.setSource(QUrl("qrc:/Samples/Features/ListRelatedFeatures/ListRelatedFeatures.qml"));
view.show();
return app.exec();}