List related features

View inC++QMLView on GitHubSample viewer app

List features related to a selected feature.

screenshot

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

  1. With a Feature, call queryRelatedFeaturesAsync on the feature's feature table.
  2. Iterate over the result's collection of RelatedFeatureQueryResult objects 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

ListRelatedFeatures.cppListRelatedFeatures.cppListRelatedFeatures.hRelatedFeature.cppRelatedFeature.hRelatedFeatureListModel.cppRelatedFeatureListModel.hListRelatedFeatures.qml
Use dark colors for code blocksCopy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// [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

#include "ListRelatedFeatures.h"

#include "Map.h"
#include "MapQuickView.h"
#include "FeatureLayer.h"
#include "ViewInsets.h"
#include "Envelope.h"
#include "QueryParameters.h"
#include "ArcGISFeature.h"
#include "ArcGISFeatureTable.h"
#include "RelatedFeatureQueryResult.h"
#include "FeatureQueryResult.h"
#include "RelatedFeature.h"
#include "RelatedFeatureListModel.h"
#include "SelectionProperties.h"
#include "Error.h"
#include "LayerListModel.h"
#include "FeatureIterator.h"
#include "ArcGISFeatureLayerInfo.h"
#include "AttributeListModel.h"
#include "SpatialReference.h"
#include "CoreTypes.h"
#include "MapTypes.h"
#include "Point.h"

#include <QFuture>
#include <QUuid>
#include <QList>
#include <QUrl>
#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();
        });
      }
    });
  });
}

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.