Query related records

Download Sample Viewer


This application demonstrates how to query related records within an ArcGISFeatureLayer. It uses a HitTestOverlay to help select the wells at the clicked location on the map. The user can further select a single well from the group. The query to get related records is executed on that well and the related tops are displayed in a table.

Code snippet

        new int[] {((Integer) selectedWellGraphic.getAttributeValue("OBJECTID")).intValue()},
        new String[] {"OBJECTID", "API_NUMBER", "ELEVATION", "FORMATION", "TOP"},
        new CallbackListener<RelatedRecordSet>() {
            public void onError(Throwable th) {
                // handle the error
            public void onCallback(RelatedRecordSet relatedRecordSet) {
                // do something with the related record set

Sample Code

/* Copyright 2014 Esri

All rights reserved under the copyright laws of the United States
and applicable international laws, treaties, and conventions.

You may freely redistribute and use this sample code, with or
without modification, provided you include the original copyright
notice and use restrictions.

See the use restrictions.*/
package com.esri.client.samples.search;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.DefaultListModel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;

import com.esri.toolkit.overlays.HitTestEvent;
import com.esri.toolkit.overlays.HitTestListener;
import com.esri.toolkit.overlays.HitTestOverlay;
import com.esri.core.geometry.Envelope;
import com.esri.core.map.CallbackListener;
import com.esri.core.map.Feature;
import com.esri.core.map.FeatureSet;
import com.esri.core.map.Graphic;
import com.esri.core.renderer.SimpleRenderer;
import com.esri.core.symbol.SimpleMarkerSymbol;
import com.esri.core.symbol.SimpleMarkerSymbol.Style;
import com.esri.map.ArcGISFeatureLayer;
import com.esri.map.ArcGISTiledMapServiceLayer;
import com.esri.map.GraphicsLayer;
import com.esri.map.JMap;
import com.esri.map.LayerList;

 * This application demonstrates the use of querying related records within an ArcGISFeatureLayer.
 * <p>
 * This application uses {@link HitTestOverlay} to highlight the wells at the clicked
 * location on a map. The user can further select a single well from the group in the list 
 * control. The query to get related records is executed on that well and the related tops 
 * are displayed in a table.
public class QueryRelatedRecordsApp {

  // symbology
  private final static SimpleMarkerSymbol SYM_WELL =
      new SimpleMarkerSymbol(Color.RED, 14, Style.CIRCLE);
  private final static SimpleMarkerSymbol SYM_WELL_SELECTED_ON_CLICK =
      new SimpleMarkerSymbol(Color.YELLOW, 14, Style.CIRCLE);

  // map
  private JMap map;
  private JPanel contentPanel;

  // resources
  private final String URL_USA_TOPO =
  private final String URL_WELLS =

  // ------------------------------------------------------------------------
  // Constructor
  // ------------------------------------------------------------------------
  public QueryRelatedRecordsApp() {


  // ------------------------------------------------------------------------
  // Core functionality
  // ------------------------------------------------------------------------
   * This method:
   * <ul>
   * <li>Queries the feature layer to get related tops of the selected well.
   * <li>Adds the result to the table model.
   * </ul>
   * @param wellsLayer The feature layer that has the wells and related tops data.
   * @param selectedWellGraphic Graphic of the currently selected well.
   * @param tblModelTops Result of the query, i.e. related tops of the selected well,
   * will be added to this model.
  private void onSelectedWellChanged(
      final ArcGISFeatureLayer wellsLayer,
      final Graphic selectedWellGraphic,
      final DefaultTableModel tblModelTops) {

    // clear previously added rows

    // -----------------------------------------------------------------------------------------
    // Query to get all tops associated to the selected well.
    // -----------------------------------------------------------------------------------------
        new int[] {((Integer) selectedWellGraphic.getAttributeValue("OBJECTID")).intValue()} ,
        3, // this is the relationship number for "Well 2 Tops" obtained from URL_WELLS
        new String[] {"OBJECTID", "API_NUMBER", "ELEVATION", "FORMATION", "TOP"},
        new CallbackListener<Map<Integer,FeatureSet>>() {

          public void onError(Throwable th) {

          public void onCallback(Map<Integer,FeatureSet> mapOfFeatures) {
            if (mapOfFeatures == null || mapOfFeatures.entrySet().size() == 0) {
                  "No related records found for this well. Please try another well.");
            } else {
              for (Map.Entry<Integer, FeatureSet> entry : mapOfFeatures.entrySet()) {
                // get required attributes from every related Top, then add them
                // as a row to the table model
                for (Graphic graphic: entry.getValue().getGraphics()) {
                  tblModelTops.addRow(new Object[] {

  // ------------------------------------------------------------------------
  // Static methods
  // ------------------------------------------------------------------------
   * Starting point of this application.
   * @param args arguments to this application.
  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        try {
          // instance of this application
          QueryRelatedRecordsApp queryRelatedRecordsApp = new QueryRelatedRecordsApp();

          // create the UI, including the map, for the application.
          JFrame appWindow = queryRelatedRecordsApp.createWindow();
        } catch (Exception e) {
          // on any error, display the stack trace.

  // ------------------------------------------------------------------------
  // Public methods
  // ------------------------------------------------------------------------
   * Creates and displays the UI, including the map, for this application.
  public JComponent createUI() throws Exception {

    // application content
    JComponent mainPane = createContentPane();
    contentPanel = new JPanel();
    contentPanel.setLayout(new BorderLayout(0, 5));

    // description
    JPanel description = createDescription();
    description.setLocation(10, 10);

    // list to display the selected Wells
    final DefaultListModel<Integer> lstModelWells = new DefaultListModel<>();
    JList<Integer> lstWells = new JList<>(lstModelWells);
    JScrollPane lstScrollWells = new JScrollPane(lstWells);

    JPanel wellPanel = new JPanel();
    wellPanel.setLayout(new BorderLayout(0, 0));
    wellPanel.setBorder(BorderFactory.createTitledBorder("Selected wells:"));

    // scrollable-table to display tops related to a well
    final DefaultTableModel tblModelTops = new DefaultTableModel() {
      private static final long serialVersionUID = 1L;
      public boolean isCellEditable(int rowIndex, int mColIndex) {
        return false;
    tblModelTops.addColumn("API Number");

    JTable tblTops = new JTable(tblModelTops);
    JScrollPane tblScrollTops = new JScrollPane(tblTops);

    JPanel topsPanel = new JPanel();
    topsPanel.setLayout(new BorderLayout(0, 0));
    topsPanel.setBorder(BorderFactory.createTitledBorder("Tops related to the well selected on the left:"));

    // create the map
    map = createMap(lstWells, tblModelTops);

    // add description and map to layered pane

    // group the above UI items into a panel
    JPanel controlPanel = new JPanel();
    controlPanel.setLayout(new BorderLayout(0, 0));
    controlPanel.add(wellPanel, BorderLayout.WEST);
    controlPanel.add(topsPanel, BorderLayout.CENTER);
    controlPanel.setPreferredSize(new Dimension(600, 150));

    contentPanel.add(mainPane, BorderLayout.CENTER);
    contentPanel.add(controlPanel, BorderLayout.SOUTH);

    return contentPanel;

  // ------------------------------------------------------------------------
  // Private methods
  // ------------------------------------------------------------------------
   * Creates a map.
   * @return a map.
  private JMap createMap(final JList<Integer> lstWells, final DefaultTableModel tblModelTops)
      throws Exception {
    final JMap jMap = new JMap();

    // -----------------------------------------------------------------------------------------
    // Base Layer - with US topology, focus on U.S by default
    // -----------------------------------------------------------------------------------------
    final ArcGISTiledMapServiceLayer baseLayer = new ArcGISTiledMapServiceLayer(URL_USA_TOPO);
    jMap.setExtent(new Envelope(-10846605, 4508331, -10830114, 4519331.298));
    LayerList layers = jMap.getLayers();

    // -----------------------------------------------------------------------------------------
    // Feature Layer - this has the locations of wells
    // -----------------------------------------------------------------------------------------
    final ArcGISFeatureLayer wellsLayer = new ArcGISFeatureLayer(URL_WELLS);
    wellsLayer.setRenderer(new SimpleRenderer(SYM_WELL));

    // -----------------------------------------------------------------------------------------
    // Graphics Layer - to highlight a selected well
    // -----------------------------------------------------------------------------------------
    final GraphicsLayer graphicsLayer = new GraphicsLayer();

    // this data will be shared between:
    // - the hit test overlay: will populate this map; and
    // - the list selection listener: will read from this map
    final Map<Integer, Graphic> selectedGraphics = new ConcurrentHashMap<>();

    // -----------------------------------------------------------------------------------------
    // Feature selection overlay - to listen to features (wells) hit by a mouse click
    // -----------------------------------------------------------------------------------------
        (DefaultListModel<Integer>) lstWells.getModel(),

    // -----------------------------------------------------------------------------------------
    // List selection listener - hook for the action to be taken when selected Well changes
    // -----------------------------------------------------------------------------------------
    lstWells.addListSelectionListener(new ListSelectionListener() {
      public void valueChanged(ListSelectionEvent e) {
        if (e.getValueIsAdjusting()) {

        Object selected = lstWells.getSelectedValue();
        if (!(selected instanceof Integer)) {

        // execute on selection from the list
        onSelectedWellChanged(wellsLayer, selectedGraphics.get(selected), tblModelTops);

    return jMap;

   * Adds an overlay for feature selection, a HitTestOverlay from the toolkit. The purpose of this
   * overlay is to handle clicked features on the map.
   * @param jMap map to which the overlay will be added to.
   * @param featureLayer the layer whose features will be selected.
   * @param graphicsLayer layer to which new graphics will be added to highlight selected
   * features.
   * @param lstWellsModel list model to which the selected wells will be added. This is reset
   * whenever selection changes.
   * @param wellIDGraphicMap this has a map of Well ID to its graphic.
  private void addFeatureSelectionOverlay(
      final JMap jMap,
      final ArcGISFeatureLayer featureLayer,
      final GraphicsLayer graphicsLayer,
      final DefaultListModel<Integer> lstWellsModel,
      final Map<Integer, Graphic> wellIDGraphicMap) {

    // create a feature selection overlay
    final HitTestOverlay featureSelectionOverlay =
        new HitTestOverlay(featureLayer);

    // add a listener to listen for clicked features
        new HitTestListener() {

           * This method is invoked whenever any feature is present at the current
           * mouse-click location on the map.
          public void featureHit(HitTestEvent event) {
            // clean up from a previous call

            // get the clicked features at the current location
            List<Feature> hitFeatures = featureSelectionOverlay.getHitFeatures();

            // get well ID of every well (feature) clicked
            for (Feature feature : hitFeatures) {
              Graphic graphic = (Graphic) feature;
              // get the well ID attribute value from the graphic
              Object id = graphic.getAttributeValue("OBJECTID");
              if (id != null) {
                Integer objectId = (Integer) graphic.getAttributeValue("OBJECTID");

                // update necessary data structures
                wellIDGraphicMap.put(objectId, graphic);

                // highlight the clicked feature by adding a new graphic to the graphics layer
                graphicsLayer.addGraphic(new Graphic(graphic.getGeometry(), SYM_WELL_SELECTED_ON_CLICK));

    // add the overlay to the JMap

   * Creates a description for this application.
   * @return description
  private JPanel createDescription() {
    JPanel descriptionContainer = new JPanel();
    descriptionContainer.setLayout(new BoxLayout(descriptionContainer, 0));
    descriptionContainer.setSize(230, 90);
    JTextArea description = new JTextArea(
        "Click on the map to select wells (symbolized by red circles). Click on " +
        "one of the selected wells in the list below to execute a query returning " +
        "related tops.");
    description.setFont(new Font("Verdana", Font.PLAIN, 11));
    description.setBackground(new Color(0, 0, 0, 180));
    descriptionContainer.setBackground(new Color(0, 0, 0, 0));
    descriptionContainer.setBorder(new LineBorder(Color.WHITE, 1, false));
    return descriptionContainer;

   * Creates a window.
   * @return a window.
  private JFrame createWindow() {
    JFrame window = new JFrame("Query Related Records Application");
    window.setBounds(100, 100, 1000, 700);
    window.getContentPane().setLayout(new BorderLayout(0, 0));
    window.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent windowEvent) {
    return window;

   * Creates a content pane.
   * @return a content pane.
  private static JLayeredPane createContentPane() {
    JLayeredPane contentPane = new JLayeredPane();
    contentPane.setBounds(100, 100, 1000, 700);
    contentPane.setLayout(new BorderLayout(0, 0));
    return contentPane;
Feedback on this topic?