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 queryRelatedFeatures on the feature's feature table.
  2. Iterate over the result's collection of queryRelatedFeaturesResults objects to get the related features and add them to a list.

Relevant API

  • Feature
  • FeatureTable
  • FeatureQueryResult
  • FeatureTable.queryRelatedFeatures
  • RelatedFeatureQueryResult

Tags

features, identify, query, related, relationship, search

Sample Code

ListRelatedFeatures.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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// [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 QtQuick
import QtQuick.Controls
import Esri.ArcGISRuntime

Rectangle {
    id: rootRectangle
    clip: true
    width: 800
    height: 600

    property FeatureLayer alaskaNationalParks: null

    MapView {
        id: mapView
        anchors.fill: parent

        Component.onCompleted: {
            // Set the focus on MapView to initially enable keyboard navigation
            forceActiveFocus();
        }

        SelectionProperties {
            color: "yellow"
        }

        // bind the insets to the attribute view so the attribution text shows when the view expands
        viewInsets.bottom: attributeView.height

        Map {
            id: map
            initUrl: "https://arcgis.com/home/item.html?id=dcc7466a91294c0ab8f7a094430ab437"

            onLoadStatusChanged: {
                if (loadStatus !== Enums.LoadStatusLoaded)
                    return;

                // get the Alaska National Parks feature layer
                map.operationalLayers.forEach(fl => {
                    if (fl.name.indexOf("- Alaska National Parks") !== -1) {
                        alaskaNationalParks = fl;
                    }
                });
            }
        }

        onMouseClicked: mouse => {
            // hide the attribute view
            attributeView.height = 0;

            // clear the list model
            relatedFeaturesModel.clear();

            // create objects required to do a selection with a query
            const clickPoint = mouse.mapPoint;
            const mapTolerance = 10 * mapView.unitsPerDIP;
            const envelope = ArcGISRuntimeEnvironment.createObject("Envelope", {
                                                                       xMin: clickPoint.x - mapTolerance,
                                                                       yMin: clickPoint.y - mapTolerance,
                                                                       xMax: clickPoint.x + mapTolerance,
                                                                       yMax: clickPoint.y + mapTolerance,
                                                                       spatialReference: map.spatialReference
                                                                   });
            const queryParams = ArcGISRuntimeEnvironment.createObject("QueryParameters");
            queryParams.geometry = envelope;
            queryParams.spatialRelationship = Enums.SpatialRelationshipIntersects;

            // clear any selections
            alaskaNationalParks.clearSelection();

            // select features
            alaskaNationalParks.selectFeaturesWithQuery(queryParams, Enums.SelectionModeNew);
        }
    }

    Connections {
        target: alaskaNationalParks

        function onSelectFeaturesStatusChanged() {
            if (alaskaNationalParks.selectFeaturesStatus === Enums.TaskStatusErrored) {
                const errorString = "Error: %1".arg(alaskaNationalParks.error.message);
                msgDialog.text = errorString;
                msgDialog.open();
                console.log(errorString);
            } else if (alaskaNationalParks.selectFeaturesStatus === Enums.TaskStatusCompleted) {
                const featureQueryResult = alaskaNationalParks.selectFeaturesResult;

                // iterate over features returned
                while (featureQueryResult.iterator.hasNext) {
                    const arcGISFeature = featureQueryResult.iterator.next();
                    const selectedTable = arcGISFeature.featureTable;

                    // connect signal
                    selectedTable.queryRelatedFeaturesStatusChanged.connect(()=> {
                        if (selectedTable.queryRelatedFeaturesStatus !== Enums.TaskStatusCompleted)
                            return;

                        const relatedFeatureQueryResultList = selectedTable.queryRelatedFeaturesResults;

                        // iterate over returned RelatedFeatureQueryResults
                        for (let i = 0; i < relatedFeatureQueryResultList.length; i++) {

                            // iterate over Features returned
                            const iter = relatedFeatureQueryResultList[i].iterator;
                            while (iter.hasNext) {
                                const feat = iter.next();
                                const displayFieldName = feat.featureTable.layerInfo.displayFieldName;
                                const serviceLayerName = feat.featureTable.layerInfo.serviceLayerName;
                                const displayFieldValue = feat.attributes.attributeValue(displayFieldName);

                                // add the related feature info to a list model
                                const listElement = {
                                    "displayFieldName" : displayFieldName,
                                    "displayFieldValue" : displayFieldValue,
                                    "serviceLayerName" : serviceLayerName
                                };
                                relatedFeaturesModel.append(listElement);
                            }
                        }

                        // show the attribute view
                        attributeView.height = 200
                    });

                    // zoom to the feature
                    mapView.setViewpointGeometryAndPadding(arcGISFeature.geometry, 100)

                    // query related features
                    selectedTable.queryRelatedFeatures(arcGISFeature);
                }
            }
        }
    }

    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
                        }
                    }
                }
            }
        }
    }

    ListModel {
        id: relatedFeaturesModel
    }

    Dialog {
        id: msgDialog
        modal: true
        x: Math.round(parent.width - width) / 2
        y: Math.round(parent.height - height) / 2
        standardButtons: Dialog.Ok
        property alias text : textLabel.text
        Text {
            id: textLabel
            text: "Executing geoprocessing failed."
        }
    }
}

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