List related features

View on GitHubSample viewer app

List features related to the selected feature.

Image of list related features

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

Tap 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
  • ArcGISFeatureTable.QueryRelatedFeatures
  • FeatureQueryResult
  • RelatedFeatureQueryResult

Tags

features, identify, query, related, relationship, search

Sample Code

MainActivity.java
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
/* 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.
 *
 */

package com.esri.arcgisruntime.sample.listrelatedfeatures;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;

import android.graphics.Color;
import android.graphics.Point;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.ArcGISFeature;
import com.esri.arcgisruntime.data.ArcGISFeatureTable;
import com.esri.arcgisruntime.data.Feature;
import com.esri.arcgisruntime.data.RelatedFeatureQueryResult;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.layers.Layer;
import com.esri.arcgisruntime.loadable.LoadStatus;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.GeoElement;
import com.esri.arcgisruntime.mapping.LayerList;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.IdentifyLayerResult;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.google.android.material.bottomsheet.BottomSheetBehavior;

public class MainActivity extends AppCompatActivity {

  private static final String TAG = MainActivity.class.getSimpleName();

  private final ArrayList<FeatureLayer> mOperationalLayers = new ArrayList<>();
  private final List<String> mRelatedValues = new LinkedList<>();
  private MapView mMapView;
  private ArcGISMap mArcGISMap;
  private BottomSheetBehavior mBottomSheetBehavior = null;
  private ArrayAdapter<String> mArrayAdapter;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // The View with the BottomSheetBehavior
    mBottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet));
    mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);

    // get bottomsheet collapsed height in dp
    DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
    final float dp =
        mBottomSheetBehavior.getPeekHeight() / ((float) displayMetrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);

    ListView tableList = findViewById(R.id.related_list);
    mArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mRelatedValues);
    tableList.setAdapter(mArrayAdapter);

    // get a reference to the map view
    mMapView = findViewById(R.id.mapView);
    mMapView.getSelectionProperties().setColor(Color.YELLOW);
    // create a mArcGISMap a webmap
    mArcGISMap = new ArcGISMap(getResources().getString(R.string.webmap_url));
    // set the mArcGISMap to be displayed in this view
    mMapView.setMap(mArcGISMap);
    mArcGISMap.addDoneLoadingListener(() -> {
      if (mArcGISMap.getLoadStatus() == LoadStatus.LOADED) {
        // create Features to use for listing related features
        createFeatures(mArcGISMap);
      }
    });

    mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this, mMapView) {
      @Override
      public boolean onSingleTapConfirmed(MotionEvent e) {
        // clear ListAdapter of previous results
        mArrayAdapter.clear();
        // hide the bottomsheet
        mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
        // get the point that was clicked and convert it to a point in mArcGISMap coordinates
        Point screenPoint = new Point(Math.round(e.getX()), Math.round(e.getY()));
        // get the FeatureLayer to query
        final FeatureLayer selectedLayer = mOperationalLayers.get(0);
        // get a list of related features to display
        queryRelatedFeatures(selectedLayer, screenPoint);
        return true;
      }
    });

    // respond to bottom sheet interaction
    mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
      @Override
      public void onStateChanged(@NonNull View bottomSheet, int newState) {
        if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
          // set attribution bar above bottom sheet when collapsed
          mMapView.setViewInsets(0, 0, 0, dp);
        } else {
          // set attribution bar to bottom when bottom sheet hidden or sliding
          mMapView.setViewInsets(0, 0, 0, 0);
        }
      }

      @Override
      public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        // bottom sheet sliding up or down
      }
    });
  }

  /**
   * Uses the selected FeatureLayer to get FeatureTable RelationshipInfos used to
   * QueryRelatedFeaturesAsync which returns a list of related features.
   *
   * @param featureLayer Layer selected from the Map
   * @param clickPoint   Tapped location to query
   */
  private void queryRelatedFeatures(final FeatureLayer featureLayer, Point clickPoint) {

    int tolerance = 10;
    final ListenableFuture<IdentifyLayerResult> identifyLayerResultFuture = mMapView
        .identifyLayerAsync(featureLayer, clickPoint, tolerance, false, 1);
    // clear previously selected layers
    featureLayer.clearSelection();

    identifyLayerResultFuture.addDoneListener(() -> {
      try {
        IdentifyLayerResult identifyLayerResult = identifyLayerResultFuture.get();
        for (GeoElement element : identifyLayerResult.getElements()) {
          Feature feature = (Feature) element;
          ArcGISFeature arcGISFeature = (ArcGISFeature) feature;
          ArcGISFeatureTable selectedTable = (ArcGISFeatureTable) feature.getFeatureTable();
          final ListenableFuture<List<RelatedFeatureQueryResult>> relatedFeatureQueryResultFuture = selectedTable
              .queryRelatedFeaturesAsync(arcGISFeature);
          relatedFeatureQueryResultFuture.addDoneListener(() -> {
            try {
              List<RelatedFeatureQueryResult> relatedFeatureQueryResultList = relatedFeatureQueryResultFuture.get();
              // iterate over returned RelatedFeatureQueryResults
              for (RelatedFeatureQueryResult relatedQueryResult : relatedFeatureQueryResultList) {
                // add Table Name to List
                String relatedTableName = relatedQueryResult.getRelatedTable().getTableName();
                mRelatedValues.add(relatedTableName);
                // iterate over Features returned
                for (Feature relatedFeature : relatedQueryResult) {
                  // get the Display field to use as filter on related attributes
                  ArcGISFeature agsFeature = (ArcGISFeature) relatedFeature;
                  String displayFieldName = agsFeature.getFeatureTable().getLayerInfo().getDisplayFieldName();
                  String displayFieldValue = agsFeature.getAttributes().get(displayFieldName).toString();
                  mRelatedValues.add(displayFieldValue);
                  // notify ListAdapter content has changed
                  mArrayAdapter.notifyDataSetChanged();
                }
              }
            } catch (InterruptedException | ExecutionException e) {
              String error = "Error getting related feature query result: " + e.getMessage();
              Toast.makeText(this, error, Toast.LENGTH_LONG).show();
              Log.e(TAG, error);
            }
          });
        }
      } catch (InterruptedException | ExecutionException e) {
        String error = "Error getting related feature query result: " + e.getMessage();
        Toast.makeText(this, error, Toast.LENGTH_LONG).show();
        Log.e(TAG, error);
      }
      // show the bottomsheet with results
      mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
    });
  }

  /**
   * Create Features from Layers in the Map
   *
   * @param map ArcGISMap to get Layers and Tables
   */
  private void createFeatures(ArcGISMap map) {
    LayerList layers = map.getOperationalLayers();
    // add the National Parks Feature layer to LayerList
    for (Layer layer : layers) {
      FeatureLayer fLayer = (FeatureLayer) layer;
      if (fLayer.getName().contains("Alaska National Parks")) {
        mOperationalLayers.add(fLayer);
      }
    }
  }

  @Override
  protected void onPause() {
    mMapView.pause();
    super.onPause();
  }

  @Override
  protected void onResume() {
    super.onResume();
    mMapView.resume();
  }

  @Override
  protected void onDestroy() {
    mMapView.dispose();
    super.onDestroy();
  }
}

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