Offline feature table query

Download Sample Viewer

Description

This sample demonstrates retrieval and display of data independent of any mapping. A Geodatabase object is created from a local geodatabase (.geodatabase file) and a feature table (GeodatabaseFeatureTable class) is created from the Geodatabase using the desired layer ID via getGeodatabaseFeatureTableByLayerId(LAYER_ID). Queries are then performed against the feature table. As the geodatabase is a local file on disk, this sample does not require a network connection.

Note: you must have write access to the directory containing the .geodatabase file in order for the Geodatabase object to be created, and thus for this sample to run properly. By default it is in a subdirectory of the SDK installation directory. This sample will copy the file to java.io.tmpdir if the user does not have permission to create files in the current data directory.

Code snippet


  // create the geodatabase and geodatabase feature table once
  geodatabase = new Geodatabase(GEODATABASE_PATH);
  table = geodatabase.getGeodatabaseFeatureTableByLayerId(LAYER_ID);
  
  // create the query parameters
  QueryParameters queryParameters = new QueryParameters();
  
  // execute the query.
  // this option returns result as an iterator.
  table.queryFeatures(queryParameters, callback) {
    ...
  }
  

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.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
import javax.swing.table.DefaultTableModel;

import com.esri.core.geodatabase.Geodatabase;
import com.esri.core.geodatabase.GeodatabaseFeatureTable;
import com.esri.core.map.CallbackListener;
import com.esri.core.map.Feature;
import com.esri.core.map.FeatureResult;
import com.esri.core.tasks.query.QueryParameters;
import com.esri.runtime.ArcGISRuntime;

/**
 * This sample demonstrates retrieval and display of data independent of any mapping. 
 * A Geodatabase object is created from a local geodatabase (.geodatabase file) and 
 * a feature table (GeodatabaseFeatureTable class) is created from the Geodatabase using 
 * the desired layer ID via <code>getGeodatabaseFeatureTableByLayerId(LAYER_ID)</code>. 
 * Queries are then performed against the feature table. As the geodatabase is a local 
 * file on disk, this sample does not require a network connection.
 * <p>
 * Note: you must have write access to the directory containing the geodatabase 
 * in order for the Geodatabase object to be created. By default it is in a subdirectory 
 * of the SDK installation directory. This sample will copy the file to java.io.tmpdir 
 * if the user does not have permission to create files in the current data directory.
 */
public class LocalQueryApp {

  // resources
  private static String FSP = System.getProperty("file.separator");
  private static final String RELATIVE_PATH = 
    "disconnected" + FSP + "geodatabase" + FSP + "DamageInspection6.geodatabase";
  private static final String GEODATABASE_PATH = getPathSampleData() + FSP + RELATIVE_PATH;
  private static final int LAYER_ID = 0;

  // query attributes
  private final String[] ATTR_NAMES  =
      new String[] {"OBJECTID", "PLACENAME", "FIRSTNAME", "LASTNAME", "TYPDAMAGE", "PRIMCAUSE"};
  private final String[] ATTR_HEADER =
      new String[] {"Object ID", "Place", "First Name", "Last Name", "Damage type", "Cause"};
  private JPanel contentPane;
  private GeodatabaseFeatureTable table;
  private Geodatabase geodatabase;

  // ------------------------------------------------------------------------
  // Constructor
  // ------------------------------------------------------------------------
  public LocalQueryApp() {
  }

  // ------------------------------------------------------------------------
  // Core functionality
  // ------------------------------------------------------------------------
  /**
   * Executes a query.
   * @param txtQuery Input query.
   * @param tblModelQueryResult Query result will be populated in this.
   */
  private void executeQueryInBatch(JTextField txtQuery, final DefaultTableModel tblModelQueryResult) {

    // return if input is not valid or table null
    String damageType = txtQuery.getText();
    if (table == null || damageType == null || damageType.isEmpty()) {
      return;
    }

    // initialize the result - add the column headers if not already present,
    // and clear existing rows.
    if (tblModelQueryResult.getColumnCount() == 0) {
      for (String attrHeader : ATTR_HEADER) {
        tblModelQueryResult.addColumn(attrHeader);
      }
    }
    tblModelQueryResult.setRowCount(0);

    // create a query 
    QueryParameters query = new QueryParameters();
    // specify the attributes to be fetched.
    query.setOutFields(ATTR_NAMES);
    //query.setReturnIdsOnly(true);
    // specify the query criteria.
    query.setWhere("TYPDAMAGE LIKE '%" + damageType + "%'");

    // execute the query.
    // this option returns result as an iterator.
    // use this option when fitting all features in memory may not be practical. 
    table.queryFeatures(query, new CallbackListener<FeatureResult>() {

      @Override
      public void onError(Throwable e) {
        JOptionPane.showMessageDialog(contentPane, wrap("Error: "+e.getLocalizedMessage()), "", JOptionPane.ERROR_MESSAGE);
      }

      @Override
      public void onCallback(FeatureResult objs) {
        for (Object objFeature : objs) {
          Feature feature = (Feature) objFeature;

          // get attributes' value in the same order as the header.
          Object[] rowData = new Object[ATTR_NAMES.length];
          int index = 0;
          for (String attrName : ATTR_NAMES) {
            rowData[index++] = feature.getAttributeValue(attrName);
          }
          // add one row per feature
          tblModelQueryResult.addRow(rowData);
        }
      }
    });
  }

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

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

  // ------------------------------------------------------------------------
  // Public methods
  // ------------------------------------------------------------------------
  /**
   * Creates and displays the UI, including the map, for this application.
   */
  public JComponent createUI() throws Exception {
    // application content
    contentPane = new JPanel();
    contentPane.setLayout(new BorderLayout());

    // description
    JTextField txtDescription = createDescription();
    txtDescription.setMaximumSize(new Dimension(600, 40));

    // query label
    JTextArea lblQuery = new JTextArea("Damage type contains (query is case-sensitive): ");
    lblQuery.append("(Values: Affected, Destroyed, Inaccessible,  Major, Minor)");
    lblQuery.setEditable(false);
    lblQuery.setForeground(Color.BLACK);
    lblQuery.setBackground(null);
    lblQuery.setMaximumSize(new Dimension(600, 20));

    // query input field
    final JTextField txtQuery = new JTextField();
    txtQuery.setText("Minor");
    txtQuery.setMaximumSize(new Dimension(350, 20));

    // query result - scrollable table
    final DefaultTableModel tblModelQueryResult = new DefaultTableModel();
    JTable tablQueryResult = new JTable(tblModelQueryResult);
    JScrollPane tblQueryResultScrollable = new JScrollPane(tablQueryResult);

    // query button
    JButton btnQuery = new JButton("Query");
    btnQuery.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        // execute query when button is pressed.
        executeQueryInBatch(txtQuery, tblModelQueryResult);
      }
    });
    btnQuery.setMaximumSize(new Dimension(150, 20));
    btnQuery.setAlignmentX(Component.CENTER_ALIGNMENT);

    // group the above UI items into a panel
    final JPanel controlPanel = new JPanel();
    BoxLayout boxLayout = new BoxLayout(controlPanel, BoxLayout.Y_AXIS);
    controlPanel.setLayout(boxLayout);
    controlPanel.setBorder(new LineBorder(new Color(0, 0, 0, 100), 5, false));

    controlPanel.add(txtDescription);
    controlPanel.add(lblQuery);
    controlPanel.add(txtQuery);
    controlPanel.add(btnQuery);
    controlPanel.add(tblQueryResultScrollable);

    // add the panel to the main window
    contentPane.add(controlPanel);
    
    // create the geodatabase and geodatabase feature table once
    try {
      geodatabase = new Geodatabase(GEODATABASE_PATH);
      table = geodatabase.getGeodatabaseFeatureTableByLayerId(LAYER_ID);
    } catch (Exception e) {
      JOptionPane.showMessageDialog(contentPane, 
        wrap("Error: " + e.getLocalizedMessage()) + "\r\nSee notes for this application.");
    }

    return contentPane;
  }

  // ------------------------------------------------------------------------
  // Private methods
  // ------------------------------------------------------------------------
  /**
   * Creates a description for this application.
   * @return description
   */
  private JTextField createDescription() {
    JTextField description = new JTextField(
      "This sample demonstrates retrieval and display of data independent of any mapping.");
    description.setFont(new Font("Verdana", Font.PLAIN, 11));
    description.setForeground(Color.WHITE);
    description.setBackground(Color.BLACK);
    description.setEditable(false);
    description.setHorizontalAlignment(SwingConstants.CENTER);
    return description;
  }
  
  /**
   * Creates a window, disposing of the geodatabase on application exit - you must 
   * call dispose to release any native resources.
   * 
   * @return a window.
   */
  private JFrame createWindow() {
    JFrame window = new JFrame("Local Query Application");
    window.setBounds(100, 100, 1000, 700);
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.getContentPane().setLayout(new BorderLayout(0, 0));
    window.addWindowListener(new WindowAdapter() {
      @Override
      public void windowClosing(WindowEvent windowEvent) {
        super.windowClosing(windowEvent);
        if (geodatabase != null) geodatabase.dispose();
      }
    });
    return window;
  }

  private static String getPathSampleData() {
    String dataPath = null;
    String javaPath = ArcGISRuntime.getInstallDirectory();
    if (javaPath != null) {
      if (!(javaPath.endsWith("/") || javaPath.endsWith("\\"))){
        javaPath += FSP;
      }
      dataPath = javaPath + "sdk" + FSP + "samples" + FSP + "data" + FSP;
    }
    File dataDir = new File(dataPath);
    if (!dataDir.exists()) { 
      dataPath = ".." + FSP + "data" + FSP;
      dataDir = new File(dataPath);
    }
    
    return copyIfNoCreateFileAccess(dataDir, RELATIVE_PATH);
  }
  
  /**
   * Copies file to java.io.tmpdir if the user does not have permission to create file
   * in the current dataDir.
   * @param dataDir directory containing the file to be copied.
   * @param relativeFilePath path of file to be copied relative to the dataDir.
   * @return the path of directory that contains the file copied. This will be in the java.io.tmpdir
   * if copied, otherwise it will be same as input dataDir.
   */
  private static String copyIfNoCreateFileAccess(File dataDir, String relativeFilePath) {
    // check if can create a file in the data directory.
    // can't use File.canWrite() because create is considered different from write. 
    File tempFolder = null;
    try {
      tempFolder = new File(dataDir.getAbsolutePath() + FSP + "temp");
      if (tempFolder.exists()) {
        tempFolder.delete();
      }
      tempFolder.mkdir();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    if (tempFolder != null && tempFolder.exists()) {
      tempFolder.delete();
      return dataDir.getAbsolutePath();
    }

    // no permission to create a new file in the existing data folder.
    // so copy the data file to the java.io.tmpdir folder.
    String tempDataDir = System.getProperty("java.io.tmpdir") + FSP + 
        "ArcGIS SDKs" + FSP + "java" + ArcGISRuntime.getAPIVersion() + 
        FSP + "sdk" + FSP + "samples" + FSP + "data"; 
    
    String fromFilePath = dataDir + FSP + relativeFilePath;
    String toFilePath = tempDataDir + FSP + relativeFilePath;
    FileInputStream in = null;
    FileOutputStream out = null;
    try {
      File toFile = new File(toFilePath);
      if (toFile.exists()) {
        return tempDataDir;
      }
      toFile.getParentFile().mkdirs();
      toFile.createNewFile();
      System.out.println("Copying file from " + fromFilePath + " to " + toFile.getAbsolutePath());
      in = new FileInputStream(fromFilePath);
      out = new FileOutputStream(toFile);
      byte[] byteBuf = new byte[4096]; 
      int numBytesRead;
      while ((numBytesRead = in.read(byteBuf)) != -1) {
        out.write(byteBuf, 0, numBytesRead);
      }
    } catch (Exception ex) {
      JOptionPane.showMessageDialog(null, wrap(ex.getMessage()));
    } finally {
      if (in != null) {
        try {
          in.close();
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
      if (out != null) {
        try {
          out.close();
        } catch (IOException ex) {
          ex.printStackTrace();
        }
      }
    }
    System.out.println("Copy completed.");
    return tempDataDir;
  }
  
  private static String wrap(String str) {
    // create a HTML string that wraps text when longer
    return "<html><p style='width:200px;'>" + str + "</html>";
  }
}
Feedback on this topic?