How to get a listener for many Feature Layers in ArcGIS Android?

2184
6
Jump to solution
09-13-2017 04:44 AM
GaelDurand
New Contributor

I have two Feature Layers added into a Map that I can see when I display the MapView. However, it's only the last added Feature Layer that can be processed by the Touch Listener. I cannot figure out how to make all Features Layers taken into account by the Touch Listener.

My goal is to differentiate a click on a CARTO_ETARE's feature from a click on a CARTO_PT_EAU's one.

Any help would be appreciated.

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.Feature;
import com.esri.arcgisruntime.data.FeatureQueryResult;
import com.esri.arcgisruntime.data.Geodatabase;
import com.esri.arcgisruntime.data.GeodatabaseFeatureTable;
import com.esri.arcgisruntime.data.QueryParameters;
import com.esri.arcgisruntime.geometry.Envelope;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.Basemap;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.GraphicsOverlay;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.google.android.gms.tasks.OnCompleteListener;
import com.sigpau.sigpau.R;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import static com.sigpau.sigpau.R.id.mapView;


// TIP: "Invalidate caches and Restart" in Android Studio in case of debugger unreachable

public class MainActivityDemo extends AppCompatActivity implements View.OnClickListener, OnCompleteListener<Void> {

    private MapView mMapView;
    private static File extStorDir;
    private static String extSDCardDirName;
    private static String geodbFilename;
    private static String mGeoDb;
    private Geodatabase mGeodatabase;

    // define permission to request
    private final String[] reqPermission = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};

    private ArcGISMap mMap;
    private FeatureLayer mFeatureLayer;
    private ListenableFuture<FeatureQueryResult> mFuture;

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

        // inflate MapView from layout
        mMapView = (MapView) findViewById(mapView);

        // create a map with the streets basemap
        mMap = new ArcGISMap(Basemap.Type.TOPOGRAPHIC, 48.1119800, -1.6742900, 14);
        //set an initial viewpoint

        // set the map to be displayed in the MapView
        mMapView.setMap(mMap);

        // create the path to local data
        extStorDir = Environment.getExternalStorageDirectory();
        extSDCardDirName = this.getResources().getString(R.string.config_data_sdcard_offline_dir);
        geodbFilename = this.getResources().getString(R.string.config_geodb_name);

        // full path to data
        mGeoDb = createGeoDbFilePath();

        ActivityCompat.requestPermissions(MainActivityDemo.this, reqPermission, 2);
    }

    /**
     * Handle the permissions request response
     */
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // create a new Geodatabase from local path
            mGeodatabase = new Geodatabase(mGeoDb);
            // load the geodatabase
            mGeodatabase.loadAsync();
            // add feature layer from geodatabase to the ArcGISMap
            mGeodatabase.addDoneLoadingListener(new Runnable() {
                @Override
                public void run() {
                    for (GeodatabaseFeatureTable geoDbTable : mGeodatabase.getGeodatabaseFeatureTables()){

                        ArrayList<String> list_of_tables = new ArrayList<String>();
                        list_of_tables.add("CARTO_ETARE");
                        list_of_tables.add("CARTO_PT_EAU");
                        Set<String> set = new HashSet<String>(list_of_tables);
                        if (set.contains(geoDbTable.getTableName())) {
                            mFeatureLayer = new FeatureLayer(geoDbTable);
                            mFeatureLayer.setSelectionColor(Color.YELLOW);
                            mFeatureLayer.setLabelsEnabled(true);
                            mFeatureLayer.setSelectionWidth(10);

                            //featureLayer.selectFeatures();
                            mMap.getOperationalLayers().add(mFeatureLayer);
                            mMapView.setMap(mMap);

                            // set an on touch listener to listen for click events
                            mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(MainActivityDemo.this, mMapView) {
                                @Override
                                public boolean onSingleTapConfirmed(MotionEvent e) {

                                    // get the point that was clicked and convert it to a point in map coordinates
                                    Point clickPoint = mMapView.screenToLocation(new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY())));
                                    int tolerance = 10;
                                    double mapTolerance = tolerance * mMapView.getUnitsPerDensityIndependentPixel();
                                    // create objects required to do a selection with a query
                                    Envelope envelope = new Envelope(clickPoint.getX() - mapTolerance,
                                            clickPoint.getY() - mapTolerance,
                                            clickPoint.getX() + mapTolerance,
                                            clickPoint.getY() + mapTolerance,
                                            mMap.getSpatialReference());
                                    QueryParameters query = new QueryParameters();
                                    query.setGeometry(envelope);
                                    // call select features

                                    mFuture = mFeatureLayer.selectFeaturesAsync(query, FeatureLayer.SelectionMode.ADD);
                                    // add done loading listener to fire when the selection returns
                                    mFuture.addDoneListener(new Runnable() {
                                        @Override
                                        public void run() {
                                            try {
                                                //call get on the future to get the result
                                                FeatureQueryResult result = mFuture.get();
                                                // create an Iterator
                                                Iterator<Feature> iterator = result.iterator();
                                                Feature feature;

                                                while (iterator.hasNext()) {
                                                    feature = iterator.next();
                                                    Map<String, Object> attributes = feature.getAttributes();

                                                    if(feature.getFeatureTable().getTableName().equals("CARTO_PT_EAU")) {
                                                        Toast.makeText(getApplicationContext(), Long.toString((Long)attributes.get("ID_PT_EAU")), Toast.LENGTH_SHORT).show();
                                                    }
                                                    else if(feature.getFeatureTable().getTableName().equals("CARTO_ETARE")) {
                                                        Toast.makeText(getApplicationContext(), Long.toString((Long)attributes.get("CARTO_ETARE")), Toast.LENGTH_SHORT).show();
                                                    }
                                                }
                                            } catch (Exception e) {
                                                Log.e(getResources().getString(R.string.app_name), "Select feature failed: " + e.getMessage());
                                            }
                                        }
                                    });
                                    return super.onSingleTapConfirmed(e);
                                }
                            });
                        }

                    }
                }
            });
        } else {
            // report to user that permission was denied
            Toast.makeText(MainActivityDemo.this, getResources().getString(R.string.location_permission_denied),
                    Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Create the mobile geodatabase file location and name structure
     */
    private static String createGeoDbFilePath() {
        return extStorDir.getAbsolutePath() + File.separator + extSDCardDirName + File.separator + geodbFilename;
    }
}
Tags (2)
0 Kudos
1 Solution

Accepted Solutions
AlexanderNohe1
Occasional Contributor III

Hi durand.gael‌,

The point passed to the identifyLayerAsync method is a android.graphic.point so you can pass in the screen location that was touched to do the identify.  It takes care of all the conversion to a localized point for you.  This allows you to quickly implement the touch to identify rather than having to go through the process of manually converting the point.

View solution in original post

6 Replies
AlexanderNohe1
Occasional Contributor III

Can you post your code so its on seperate lines?  It is a little hard to read here...

0 Kudos
GaelDurand
New Contributor

The update is done.

Sorry for the long line...

0 Kudos
AlexanderNohe1
Occasional Contributor III

Is there a reason that you are not using the identifyLayerAync method on the mapview?

This should return an identifyLayerResult which you can use to get all the identified features with.

0 Kudos
GaelDurand
New Contributor

Hi,

Thanks for the idea, but it's still not working because the Point passed to identifyLayersAsync is recognized as an android.graphics.point. That's weird.

What I've done so far:

                            mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(MainActivity.this, mMapView) {
                                @Override
                                public boolean onSingleTapConfirmed(MotionEvent e) {

                                    mInfoButton.setVisibility(View.GONE);
                                    mContactButton.setVisibility(View.GONE);
                                    mPlanButton.setVisibility(View.GONE);

                                    Point gps = new Point(-1.640235, 48.127568, SpatialReferences.getWgs84());
                                    final ListenableFuture<List<IdentifyLayerResult>> identifyFuture = mMapView.identifyLayersAsync(gps, 20, false, 25);

// add a listener to the future
                                    identifyFuture.addDoneListener(new Runnable() {
                                        @Override
                                        public void run() {
                                            try {
                                                // get the identify results from the future - returns when the operation is complete
                                                List<IdentifyLayerResult> identifyLayersResults = identifyFuture.get();

                                                // iterate all the layers in the identify result
                                                for (IdentifyLayerResult identifyLayerResult : identifyLayersResults) {

                                                    // iterate each result in each identified layer, and check for Feature results
                                                    for (GeoElement identifiedElement : identifyLayerResult.getElements()) {
                                                        if (identifiedElement instanceof Feature) {

                                                        }
                                                    }
                                                }
                                            } catch (InterruptedException | ExecutionException ex) {
                                                Log.e(getResources().getString(R.string.app_name), ex.getMessage());
                                            }
                                        }
                                    });
0 Kudos
AlexanderNohe1
Occasional Contributor III

Hi durand.gael‌,

The point passed to the identifyLayerAsync method is a android.graphic.point so you can pass in the screen location that was touched to do the identify.  It takes care of all the conversion to a localized point for you.  This allows you to quickly implement the touch to identify rather than having to go through the process of manually converting the point.

GaelDurand
New Contributor

Hi Alexander,

Thanks for the answer. 

This is how I create the point from the screen touch: 

final ListenableFuture<List<IdentifyLayerResult>> identifyFuture = mMapView.identifyLayersAsync(new android.graphics.Point(Math.round(e.getX()), Math.round(e.getY())), 20, false, 25);

0 Kudos